Fix broken merge
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028
4029
4030 // nasty IE9 hack - what a pile of crap that is..
4031
4032  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4033     Range.prototype.createContextualFragment = function (html) {
4034         var doc = window.document;
4035         var container = doc.createElement("div");
4036         container.innerHTML = html;
4037         var frag = doc.createDocumentFragment(), n;
4038         while ((n = container.firstChild)) {
4039             frag.appendChild(n);
4040         }
4041         return frag;
4042     };
4043 }
4044
4045 /**
4046  * @class Roo.DomHelper
4047  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4048  * 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>.
4049  * @singleton
4050  */
4051 Roo.DomHelper = function(){
4052     var tempTableEl = null;
4053     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4054     var tableRe = /^table|tbody|tr|td$/i;
4055     var xmlns = {};
4056     // build as innerHTML where available
4057     /** @ignore */
4058     var createHtml = function(o){
4059         if(typeof o == 'string'){
4060             return o;
4061         }
4062         var b = "";
4063         if(!o.tag){
4064             o.tag = "div";
4065         }
4066         b += "<" + o.tag;
4067         for(var attr in o){
4068             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4069             if(attr == "style"){
4070                 var s = o["style"];
4071                 if(typeof s == "function"){
4072                     s = s.call();
4073                 }
4074                 if(typeof s == "string"){
4075                     b += ' style="' + s + '"';
4076                 }else if(typeof s == "object"){
4077                     b += ' style="';
4078                     for(var key in s){
4079                         if(typeof s[key] != "function"){
4080                             b += key + ":" + s[key] + ";";
4081                         }
4082                     }
4083                     b += '"';
4084                 }
4085             }else{
4086                 if(attr == "cls"){
4087                     b += ' class="' + o["cls"] + '"';
4088                 }else if(attr == "htmlFor"){
4089                     b += ' for="' + o["htmlFor"] + '"';
4090                 }else{
4091                     b += " " + attr + '="' + o[attr] + '"';
4092                 }
4093             }
4094         }
4095         if(emptyTags.test(o.tag)){
4096             b += "/>";
4097         }else{
4098             b += ">";
4099             var cn = o.children || o.cn;
4100             if(cn){
4101                 //http://bugs.kde.org/show_bug.cgi?id=71506
4102                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4103                     for(var i = 0, len = cn.length; i < len; i++) {
4104                         b += createHtml(cn[i], b);
4105                     }
4106                 }else{
4107                     b += createHtml(cn, b);
4108                 }
4109             }
4110             if(o.html){
4111                 b += o.html;
4112             }
4113             b += "</" + o.tag + ">";
4114         }
4115         return b;
4116     };
4117
4118     // build as dom
4119     /** @ignore */
4120     var createDom = function(o, parentNode){
4121          
4122         // defininition craeted..
4123         var ns = false;
4124         if (o.ns && o.ns != 'html') {
4125                
4126             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4127                 xmlns[o.ns] = o.xmlns;
4128                 ns = o.xmlns;
4129             }
4130             if (typeof(xmlns[o.ns]) == 'undefined') {
4131                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4132             }
4133             ns = xmlns[o.ns];
4134         }
4135         
4136         
4137         if (typeof(o) == 'string') {
4138             return parentNode.appendChild(document.createTextNode(o));
4139         }
4140         o.tag = o.tag || div;
4141         if (o.ns && Roo.isIE) {
4142             ns = false;
4143             o.tag = o.ns + ':' + o.tag;
4144             
4145         }
4146         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4147         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4148         for(var attr in o){
4149             
4150             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4151                     attr == "style" || typeof o[attr] == "function") continue;
4152                     
4153             if(attr=="cls" && Roo.isIE){
4154                 el.className = o["cls"];
4155             }else{
4156                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4157                 else el[attr] = o[attr];
4158             }
4159         }
4160         Roo.DomHelper.applyStyles(el, o.style);
4161         var cn = o.children || o.cn;
4162         if(cn){
4163             //http://bugs.kde.org/show_bug.cgi?id=71506
4164              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4165                 for(var i = 0, len = cn.length; i < len; i++) {
4166                     createDom(cn[i], el);
4167                 }
4168             }else{
4169                 createDom(cn, el);
4170             }
4171         }
4172         if(o.html){
4173             el.innerHTML = o.html;
4174         }
4175         if(parentNode){
4176            parentNode.appendChild(el);
4177         }
4178         return el;
4179     };
4180
4181     var ieTable = function(depth, s, h, e){
4182         tempTableEl.innerHTML = [s, h, e].join('');
4183         var i = -1, el = tempTableEl;
4184         while(++i < depth){
4185             el = el.firstChild;
4186         }
4187         return el;
4188     };
4189
4190     // kill repeat to save bytes
4191     var ts = '<table>',
4192         te = '</table>',
4193         tbs = ts+'<tbody>',
4194         tbe = '</tbody>'+te,
4195         trs = tbs + '<tr>',
4196         tre = '</tr>'+tbe;
4197
4198     /**
4199      * @ignore
4200      * Nasty code for IE's broken table implementation
4201      */
4202     var insertIntoTable = function(tag, where, el, html){
4203         if(!tempTableEl){
4204             tempTableEl = document.createElement('div');
4205         }
4206         var node;
4207         var before = null;
4208         if(tag == 'td'){
4209             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4210                 return;
4211             }
4212             if(where == 'beforebegin'){
4213                 before = el;
4214                 el = el.parentNode;
4215             } else{
4216                 before = el.nextSibling;
4217                 el = el.parentNode;
4218             }
4219             node = ieTable(4, trs, html, tre);
4220         }
4221         else if(tag == 'tr'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(3, tbs, html, tbe);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(3, tbs, html, tbe);
4230             } else{ // INTO a TR
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(4, trs, html, tre);
4235             }
4236         } else if(tag == 'tbody'){
4237             if(where == 'beforebegin'){
4238                 before = el;
4239                 el = el.parentNode;
4240                 node = ieTable(2, ts, html, te);
4241             } else if(where == 'afterend'){
4242                 before = el.nextSibling;
4243                 el = el.parentNode;
4244                 node = ieTable(2, ts, html, te);
4245             } else{
4246                 if(where == 'afterbegin'){
4247                     before = el.firstChild;
4248                 }
4249                 node = ieTable(3, tbs, html, tbe);
4250             }
4251         } else{ // TABLE
4252             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4253                 return;
4254             }
4255             if(where == 'afterbegin'){
4256                 before = el.firstChild;
4257             }
4258             node = ieTable(2, ts, html, te);
4259         }
4260         el.insertBefore(node, before);
4261         return node;
4262     };
4263
4264     return {
4265     /** True to force the use of DOM instead of html fragments @type Boolean */
4266     useDom : false,
4267
4268     /**
4269      * Returns the markup for the passed Element(s) config
4270      * @param {Object} o The Dom object spec (and children)
4271      * @return {String}
4272      */
4273     markup : function(o){
4274         return createHtml(o);
4275     },
4276
4277     /**
4278      * Applies a style specification to an element
4279      * @param {String/HTMLElement} el The element to apply styles to
4280      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4281      * a function which returns such a specification.
4282      */
4283     applyStyles : function(el, styles){
4284         if(styles){
4285            el = Roo.fly(el);
4286            if(typeof styles == "string"){
4287                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4288                var matches;
4289                while ((matches = re.exec(styles)) != null){
4290                    el.setStyle(matches[1], matches[2]);
4291                }
4292            }else if (typeof styles == "object"){
4293                for (var style in styles){
4294                   el.setStyle(style, styles[style]);
4295                }
4296            }else if (typeof styles == "function"){
4297                 Roo.DomHelper.applyStyles(el, styles.call());
4298            }
4299         }
4300     },
4301
4302     /**
4303      * Inserts an HTML fragment into the Dom
4304      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4305      * @param {HTMLElement} el The context element
4306      * @param {String} html The HTML fragmenet
4307      * @return {HTMLElement} The new node
4308      */
4309     insertHtml : function(where, el, html){
4310         where = where.toLowerCase();
4311         if(el.insertAdjacentHTML){
4312             if(tableRe.test(el.tagName)){
4313                 var rs;
4314                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4315                     return rs;
4316                 }
4317             }
4318             switch(where){
4319                 case "beforebegin":
4320                     el.insertAdjacentHTML('BeforeBegin', html);
4321                     return el.previousSibling;
4322                 case "afterbegin":
4323                     el.insertAdjacentHTML('AfterBegin', html);
4324                     return el.firstChild;
4325                 case "beforeend":
4326                     el.insertAdjacentHTML('BeforeEnd', html);
4327                     return el.lastChild;
4328                 case "afterend":
4329                     el.insertAdjacentHTML('AfterEnd', html);
4330                     return el.nextSibling;
4331             }
4332             throw 'Illegal insertion point -> "' + where + '"';
4333         }
4334         var range = el.ownerDocument.createRange();
4335         var frag;
4336         switch(where){
4337              case "beforebegin":
4338                 range.setStartBefore(el);
4339                 frag = range.createContextualFragment(html);
4340                 el.parentNode.insertBefore(frag, el);
4341                 return el.previousSibling;
4342              case "afterbegin":
4343                 if(el.firstChild){
4344                     range.setStartBefore(el.firstChild);
4345                     frag = range.createContextualFragment(html);
4346                     el.insertBefore(frag, el.firstChild);
4347                     return el.firstChild;
4348                 }else{
4349                     el.innerHTML = html;
4350                     return el.firstChild;
4351                 }
4352             case "beforeend":
4353                 if(el.lastChild){
4354                     range.setStartAfter(el.lastChild);
4355                     frag = range.createContextualFragment(html);
4356                     el.appendChild(frag);
4357                     return el.lastChild;
4358                 }else{
4359                     el.innerHTML = html;
4360                     return el.lastChild;
4361                 }
4362             case "afterend":
4363                 range.setStartAfter(el);
4364                 frag = range.createContextualFragment(html);
4365                 el.parentNode.insertBefore(frag, el.nextSibling);
4366                 return el.nextSibling;
4367             }
4368             throw 'Illegal insertion point -> "' + where + '"';
4369     },
4370
4371     /**
4372      * Creates new Dom element(s) and inserts them before el
4373      * @param {String/HTMLElement/Element} el The context element
4374      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4375      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4376      * @return {HTMLElement/Roo.Element} The new node
4377      */
4378     insertBefore : function(el, o, returnElement){
4379         return this.doInsert(el, o, returnElement, "beforeBegin");
4380     },
4381
4382     /**
4383      * Creates new Dom element(s) and inserts them after el
4384      * @param {String/HTMLElement/Element} el The context element
4385      * @param {Object} o The Dom object spec (and children)
4386      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4387      * @return {HTMLElement/Roo.Element} The new node
4388      */
4389     insertAfter : function(el, o, returnElement){
4390         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4391     },
4392
4393     /**
4394      * Creates new Dom element(s) and inserts them as the first child of el
4395      * @param {String/HTMLElement/Element} el The context element
4396      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4397      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4398      * @return {HTMLElement/Roo.Element} The new node
4399      */
4400     insertFirst : function(el, o, returnElement){
4401         return this.doInsert(el, o, returnElement, "afterBegin");
4402     },
4403
4404     // private
4405     doInsert : function(el, o, returnElement, pos, sibling){
4406         el = Roo.getDom(el);
4407         var newNode;
4408         if(this.useDom || o.ns){
4409             newNode = createDom(o, null);
4410             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4411         }else{
4412             var html = createHtml(o);
4413             newNode = this.insertHtml(pos, el, html);
4414         }
4415         return returnElement ? Roo.get(newNode, true) : newNode;
4416     },
4417
4418     /**
4419      * Creates new Dom element(s) and appends them to el
4420      * @param {String/HTMLElement/Element} el The context element
4421      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4422      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4423      * @return {HTMLElement/Roo.Element} The new node
4424      */
4425     append : function(el, o, returnElement){
4426         el = Roo.getDom(el);
4427         var newNode;
4428         if(this.useDom || o.ns){
4429             newNode = createDom(o, null);
4430             el.appendChild(newNode);
4431         }else{
4432             var html = createHtml(o);
4433             newNode = this.insertHtml("beforeEnd", el, html);
4434         }
4435         return returnElement ? Roo.get(newNode, true) : newNode;
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and overwrites the contents of el with them
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     overwrite : function(el, o, returnElement){
4446         el = Roo.getDom(el);
4447         if (o.ns) {
4448           
4449             while (el.childNodes.length) {
4450                 el.removeChild(el.firstChild);
4451             }
4452             createDom(o, el);
4453         } else {
4454             el.innerHTML = createHtml(o);   
4455         }
4456         
4457         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4458     },
4459
4460     /**
4461      * Creates a new Roo.DomHelper.Template from the Dom object spec
4462      * @param {Object} o The Dom object spec (and children)
4463      * @return {Roo.DomHelper.Template} The new template
4464      */
4465     createTemplate : function(o){
4466         var html = createHtml(o);
4467         return new Roo.Template(html);
4468     }
4469     };
4470 }();
4471 /*
4472  * Based on:
4473  * Ext JS Library 1.1.1
4474  * Copyright(c) 2006-2007, Ext JS, LLC.
4475  *
4476  * Originally Released Under LGPL - original licence link has changed is not relivant.
4477  *
4478  * Fork - LGPL
4479  * <script type="text/javascript">
4480  */
4481  
4482 /**
4483 * @class Roo.Template
4484 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4485 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4486 * Usage:
4487 <pre><code>
4488 var t = new Roo.Template({
4489     html :  '&lt;div name="{id}"&gt;' + 
4490         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4491         '&lt;/div&gt;',
4492     myformat: function (value, allValues) {
4493         return 'XX' + value;
4494     }
4495 });
4496 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4497 </code></pre>
4498 * 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>. 
4499 * @constructor
4500 * @param {Object} cfg - Configuration object.
4501 */
4502 Roo.Template = function(cfg){
4503     // BC!
4504     if(cfg instanceof Array){
4505         cfg = cfg.join("");
4506     }else if(arguments.length > 1){
4507         cfg = Array.prototype.join.call(arguments, "");
4508     }
4509     
4510     
4511     if (typeof(cfg) == 'object') {
4512         Roo.apply(this,cfg)
4513     } else {
4514         // bc
4515         this.html = cfg;
4516     }
4517     
4518     
4519 };
4520 Roo.Template.prototype = {
4521     
4522     /**
4523      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4524      */
4525     html : '',
4526     /**
4527      * Returns an HTML fragment of this template with the specified values applied.
4528      * @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'})
4529      * @return {String} The HTML fragment
4530      */
4531     applyTemplate : function(values){
4532         try {
4533             
4534             if(this.compiled){
4535                 return this.compiled(values);
4536             }
4537             var useF = this.disableFormats !== true;
4538             var fm = Roo.util.Format, tpl = this;
4539             var fn = function(m, name, format, args){
4540                 if(format && useF){
4541                     if(format.substr(0, 5) == "this."){
4542                         return tpl.call(format.substr(5), values[name], values);
4543                     }else{
4544                         if(args){
4545                             // quoted values are required for strings in compiled templates, 
4546                             // but for non compiled we need to strip them
4547                             // quoted reversed for jsmin
4548                             var re = /^\s*['"](.*)["']\s*$/;
4549                             args = args.split(',');
4550                             for(var i = 0, len = args.length; i < len; i++){
4551                                 args[i] = args[i].replace(re, "$1");
4552                             }
4553                             args = [values[name]].concat(args);
4554                         }else{
4555                             args = [values[name]];
4556                         }
4557                         return fm[format].apply(fm, args);
4558                     }
4559                 }else{
4560                     return values[name] !== undefined ? values[name] : "";
4561                 }
4562             };
4563             return this.html.replace(this.re, fn);
4564         } catch (e) {
4565             Roo.log(e);
4566             throw e;
4567         }
4568          
4569     },
4570     
4571     /**
4572      * Sets the HTML used as the template and optionally compiles it.
4573      * @param {String} html
4574      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4575      * @return {Roo.Template} this
4576      */
4577     set : function(html, compile){
4578         this.html = html;
4579         this.compiled = null;
4580         if(compile){
4581             this.compile();
4582         }
4583         return this;
4584     },
4585     
4586     /**
4587      * True to disable format functions (defaults to false)
4588      * @type Boolean
4589      */
4590     disableFormats : false,
4591     
4592     /**
4593     * The regular expression used to match template variables 
4594     * @type RegExp
4595     * @property 
4596     */
4597     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4598     
4599     /**
4600      * Compiles the template into an internal function, eliminating the RegEx overhead.
4601      * @return {Roo.Template} this
4602      */
4603     compile : function(){
4604         var fm = Roo.util.Format;
4605         var useF = this.disableFormats !== true;
4606         var sep = Roo.isGecko ? "+" : ",";
4607         var fn = function(m, name, format, args){
4608             if(format && useF){
4609                 args = args ? ',' + args : "";
4610                 if(format.substr(0, 5) != "this."){
4611                     format = "fm." + format + '(';
4612                 }else{
4613                     format = 'this.call("'+ format.substr(5) + '", ';
4614                     args = ", values";
4615                 }
4616             }else{
4617                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4618             }
4619             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4620         };
4621         var body;
4622         // branched to use + in gecko and [].join() in others
4623         if(Roo.isGecko){
4624             body = "this.compiled = function(values){ return '" +
4625                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4626                     "';};";
4627         }else{
4628             body = ["this.compiled = function(values){ return ['"];
4629             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4630             body.push("'].join('');};");
4631             body = body.join('');
4632         }
4633         /**
4634          * eval:var:values
4635          * eval:var:fm
4636          */
4637         eval(body);
4638         return this;
4639     },
4640     
4641     // private function used to call members
4642     call : function(fnName, value, allValues){
4643         return this[fnName](value, allValues);
4644     },
4645     
4646     /**
4647      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4648      * @param {String/HTMLElement/Roo.Element} el The context element
4649      * @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'})
4650      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4651      * @return {HTMLElement/Roo.Element} The new node or Element
4652      */
4653     insertFirst: function(el, values, returnElement){
4654         return this.doInsert('afterBegin', el, values, returnElement);
4655     },
4656
4657     /**
4658      * Applies the supplied values to the template and inserts the new node(s) before el.
4659      * @param {String/HTMLElement/Roo.Element} el The context element
4660      * @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'})
4661      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4662      * @return {HTMLElement/Roo.Element} The new node or Element
4663      */
4664     insertBefore: function(el, values, returnElement){
4665         return this.doInsert('beforeBegin', el, values, returnElement);
4666     },
4667
4668     /**
4669      * Applies the supplied values to the template and inserts the new node(s) after el.
4670      * @param {String/HTMLElement/Roo.Element} el The context element
4671      * @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'})
4672      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4673      * @return {HTMLElement/Roo.Element} The new node or Element
4674      */
4675     insertAfter : function(el, values, returnElement){
4676         return this.doInsert('afterEnd', el, values, returnElement);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and appends the new node(s) to 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     append : function(el, values, returnElement){
4687         return this.doInsert('beforeEnd', el, values, returnElement);
4688     },
4689
4690     doInsert : function(where, el, values, returnEl){
4691         el = Roo.getDom(el);
4692         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4693         return returnEl ? Roo.get(newNode, true) : newNode;
4694     },
4695
4696     /**
4697      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4698      * @param {String/HTMLElement/Roo.Element} el The context element
4699      * @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'})
4700      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4701      * @return {HTMLElement/Roo.Element} The new node or Element
4702      */
4703     overwrite : function(el, values, returnElement){
4704         el = Roo.getDom(el);
4705         el.innerHTML = this.applyTemplate(values);
4706         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4707     }
4708 };
4709 /**
4710  * Alias for {@link #applyTemplate}
4711  * @method
4712  */
4713 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4714
4715 // backwards compat
4716 Roo.DomHelper.Template = Roo.Template;
4717
4718 /**
4719  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4720  * @param {String/HTMLElement} el A DOM element or its id
4721  * @returns {Roo.Template} The created template
4722  * @static
4723  */
4724 Roo.Template.from = function(el){
4725     el = Roo.getDom(el);
4726     return new Roo.Template(el.value || el.innerHTML);
4727 };/*
4728  * Based on:
4729  * Ext JS Library 1.1.1
4730  * Copyright(c) 2006-2007, Ext JS, LLC.
4731  *
4732  * Originally Released Under LGPL - original licence link has changed is not relivant.
4733  *
4734  * Fork - LGPL
4735  * <script type="text/javascript">
4736  */
4737  
4738
4739 /*
4740  * This is code is also distributed under MIT license for use
4741  * with jQuery and prototype JavaScript libraries.
4742  */
4743 /**
4744  * @class Roo.DomQuery
4745 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).
4746 <p>
4747 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>
4748
4749 <p>
4750 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.
4751 </p>
4752 <h4>Element Selectors:</h4>
4753 <ul class="list">
4754     <li> <b>*</b> any element</li>
4755     <li> <b>E</b> an element with the tag E</li>
4756     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4757     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4758     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4759     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4760 </ul>
4761 <h4>Attribute Selectors:</h4>
4762 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4763 <ul class="list">
4764     <li> <b>E[foo]</b> has an attribute "foo"</li>
4765     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4766     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4767     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4768     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4769     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4770     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4771 </ul>
4772 <h4>Pseudo Classes:</h4>
4773 <ul class="list">
4774     <li> <b>E:first-child</b> E is the first child of its parent</li>
4775     <li> <b>E:last-child</b> E is the last child of its parent</li>
4776     <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>
4777     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4778     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4779     <li> <b>E:only-child</b> E is the only child of its parent</li>
4780     <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>
4781     <li> <b>E:first</b> the first E in the resultset</li>
4782     <li> <b>E:last</b> the last E in the resultset</li>
4783     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4784     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4785     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4786     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4787     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4788     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4789     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4790     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4791     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4792 </ul>
4793 <h4>CSS Value Selectors:</h4>
4794 <ul class="list">
4795     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4796     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4797     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4798     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4799     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4800     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4801 </ul>
4802  * @singleton
4803  */
4804 Roo.DomQuery = function(){
4805     var cache = {}, simpleCache = {}, valueCache = {};
4806     var nonSpace = /\S/;
4807     var trimRe = /^\s+|\s+$/g;
4808     var tplRe = /\{(\d+)\}/g;
4809     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4810     var tagTokenRe = /^(#)?([\w-\*]+)/;
4811     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4812
4813     function child(p, index){
4814         var i = 0;
4815         var n = p.firstChild;
4816         while(n){
4817             if(n.nodeType == 1){
4818                if(++i == index){
4819                    return n;
4820                }
4821             }
4822             n = n.nextSibling;
4823         }
4824         return null;
4825     };
4826
4827     function next(n){
4828         while((n = n.nextSibling) && n.nodeType != 1);
4829         return n;
4830     };
4831
4832     function prev(n){
4833         while((n = n.previousSibling) && n.nodeType != 1);
4834         return n;
4835     };
4836
4837     function children(d){
4838         var n = d.firstChild, ni = -1;
4839             while(n){
4840                 var nx = n.nextSibling;
4841                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4842                     d.removeChild(n);
4843                 }else{
4844                     n.nodeIndex = ++ni;
4845                 }
4846                 n = nx;
4847             }
4848             return this;
4849         };
4850
4851     function byClassName(c, a, v){
4852         if(!v){
4853             return c;
4854         }
4855         var r = [], ri = -1, cn;
4856         for(var i = 0, ci; ci = c[i]; i++){
4857             if((' '+ci.className+' ').indexOf(v) != -1){
4858                 r[++ri] = ci;
4859             }
4860         }
4861         return r;
4862     };
4863
4864     function attrValue(n, attr){
4865         if(!n.tagName && typeof n.length != "undefined"){
4866             n = n[0];
4867         }
4868         if(!n){
4869             return null;
4870         }
4871         if(attr == "for"){
4872             return n.htmlFor;
4873         }
4874         if(attr == "class" || attr == "className"){
4875             return n.className;
4876         }
4877         return n.getAttribute(attr) || n[attr];
4878
4879     };
4880
4881     function getNodes(ns, mode, tagName){
4882         var result = [], ri = -1, cs;
4883         if(!ns){
4884             return result;
4885         }
4886         tagName = tagName || "*";
4887         if(typeof ns.getElementsByTagName != "undefined"){
4888             ns = [ns];
4889         }
4890         if(!mode){
4891             for(var i = 0, ni; ni = ns[i]; i++){
4892                 cs = ni.getElementsByTagName(tagName);
4893                 for(var j = 0, ci; ci = cs[j]; j++){
4894                     result[++ri] = ci;
4895                 }
4896             }
4897         }else if(mode == "/" || mode == ">"){
4898             var utag = tagName.toUpperCase();
4899             for(var i = 0, ni, cn; ni = ns[i]; i++){
4900                 cn = ni.children || ni.childNodes;
4901                 for(var j = 0, cj; cj = cn[j]; j++){
4902                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4903                         result[++ri] = cj;
4904                     }
4905                 }
4906             }
4907         }else if(mode == "+"){
4908             var utag = tagName.toUpperCase();
4909             for(var i = 0, n; n = ns[i]; i++){
4910                 while((n = n.nextSibling) && n.nodeType != 1);
4911                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4912                     result[++ri] = n;
4913                 }
4914             }
4915         }else if(mode == "~"){
4916             for(var i = 0, n; n = ns[i]; i++){
4917                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4918                 if(n){
4919                     result[++ri] = n;
4920                 }
4921             }
4922         }
4923         return result;
4924     };
4925
4926     function concat(a, b){
4927         if(b.slice){
4928             return a.concat(b);
4929         }
4930         for(var i = 0, l = b.length; i < l; i++){
4931             a[a.length] = b[i];
4932         }
4933         return a;
4934     }
4935
4936     function byTag(cs, tagName){
4937         if(cs.tagName || cs == document){
4938             cs = [cs];
4939         }
4940         if(!tagName){
4941             return cs;
4942         }
4943         var r = [], ri = -1;
4944         tagName = tagName.toLowerCase();
4945         for(var i = 0, ci; ci = cs[i]; i++){
4946             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4947                 r[++ri] = ci;
4948             }
4949         }
4950         return r;
4951     };
4952
4953     function byId(cs, attr, id){
4954         if(cs.tagName || cs == document){
4955             cs = [cs];
4956         }
4957         if(!id){
4958             return cs;
4959         }
4960         var r = [], ri = -1;
4961         for(var i = 0,ci; ci = cs[i]; i++){
4962             if(ci && ci.id == id){
4963                 r[++ri] = ci;
4964                 return r;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function byAttribute(cs, attr, value, op, custom){
4971         var r = [], ri = -1, st = custom=="{";
4972         var f = Roo.DomQuery.operators[op];
4973         for(var i = 0, ci; ci = cs[i]; i++){
4974             var a;
4975             if(st){
4976                 a = Roo.DomQuery.getStyle(ci, attr);
4977             }
4978             else if(attr == "class" || attr == "className"){
4979                 a = ci.className;
4980             }else if(attr == "for"){
4981                 a = ci.htmlFor;
4982             }else if(attr == "href"){
4983                 a = ci.getAttribute("href", 2);
4984             }else{
4985                 a = ci.getAttribute(attr);
4986             }
4987             if((f && f(a, value)) || (!f && a)){
4988                 r[++ri] = ci;
4989             }
4990         }
4991         return r;
4992     };
4993
4994     function byPseudo(cs, name, value){
4995         return Roo.DomQuery.pseudos[name](cs, value);
4996     };
4997
4998     // This is for IE MSXML which does not support expandos.
4999     // IE runs the same speed using setAttribute, however FF slows way down
5000     // and Safari completely fails so they need to continue to use expandos.
5001     var isIE = window.ActiveXObject ? true : false;
5002
5003     // this eval is stop the compressor from
5004     // renaming the variable to something shorter
5005     
5006     /** eval:var:batch */
5007     var batch = 30803; 
5008
5009     var key = 30803;
5010
5011     function nodupIEXml(cs){
5012         var d = ++key;
5013         cs[0].setAttribute("_nodup", d);
5014         var r = [cs[0]];
5015         for(var i = 1, len = cs.length; i < len; i++){
5016             var c = cs[i];
5017             if(!c.getAttribute("_nodup") != d){
5018                 c.setAttribute("_nodup", d);
5019                 r[r.length] = c;
5020             }
5021         }
5022         for(var i = 0, len = cs.length; i < len; i++){
5023             cs[i].removeAttribute("_nodup");
5024         }
5025         return r;
5026     }
5027
5028     function nodup(cs){
5029         if(!cs){
5030             return [];
5031         }
5032         var len = cs.length, c, i, r = cs, cj, ri = -1;
5033         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5034             return cs;
5035         }
5036         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5037             return nodupIEXml(cs);
5038         }
5039         var d = ++key;
5040         cs[0]._nodup = d;
5041         for(i = 1; c = cs[i]; i++){
5042             if(c._nodup != d){
5043                 c._nodup = d;
5044             }else{
5045                 r = [];
5046                 for(var j = 0; j < i; j++){
5047                     r[++ri] = cs[j];
5048                 }
5049                 for(j = i+1; cj = cs[j]; j++){
5050                     if(cj._nodup != d){
5051                         cj._nodup = d;
5052                         r[++ri] = cj;
5053                     }
5054                 }
5055                 return r;
5056             }
5057         }
5058         return r;
5059     }
5060
5061     function quickDiffIEXml(c1, c2){
5062         var d = ++key;
5063         for(var i = 0, len = c1.length; i < len; i++){
5064             c1[i].setAttribute("_qdiff", d);
5065         }
5066         var r = [];
5067         for(var i = 0, len = c2.length; i < len; i++){
5068             if(c2[i].getAttribute("_qdiff") != d){
5069                 r[r.length] = c2[i];
5070             }
5071         }
5072         for(var i = 0, len = c1.length; i < len; i++){
5073            c1[i].removeAttribute("_qdiff");
5074         }
5075         return r;
5076     }
5077
5078     function quickDiff(c1, c2){
5079         var len1 = c1.length;
5080         if(!len1){
5081             return c2;
5082         }
5083         if(isIE && c1[0].selectSingleNode){
5084             return quickDiffIEXml(c1, c2);
5085         }
5086         var d = ++key;
5087         for(var i = 0; i < len1; i++){
5088             c1[i]._qdiff = d;
5089         }
5090         var r = [];
5091         for(var i = 0, len = c2.length; i < len; i++){
5092             if(c2[i]._qdiff != d){
5093                 r[r.length] = c2[i];
5094             }
5095         }
5096         return r;
5097     }
5098
5099     function quickId(ns, mode, root, id){
5100         if(ns == root){
5101            var d = root.ownerDocument || root;
5102            return d.getElementById(id);
5103         }
5104         ns = getNodes(ns, mode, "*");
5105         return byId(ns, null, id);
5106     }
5107
5108     return {
5109         getStyle : function(el, name){
5110             return Roo.fly(el).getStyle(name);
5111         },
5112         /**
5113          * Compiles a selector/xpath query into a reusable function. The returned function
5114          * takes one parameter "root" (optional), which is the context node from where the query should start.
5115          * @param {String} selector The selector/xpath query
5116          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5117          * @return {Function}
5118          */
5119         compile : function(path, type){
5120             type = type || "select";
5121             
5122             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5123             var q = path, mode, lq;
5124             var tk = Roo.DomQuery.matchers;
5125             var tklen = tk.length;
5126             var mm;
5127
5128             // accept leading mode switch
5129             var lmode = q.match(modeRe);
5130             if(lmode && lmode[1]){
5131                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5132                 q = q.replace(lmode[1], "");
5133             }
5134             // strip leading slashes
5135             while(path.substr(0, 1)=="/"){
5136                 path = path.substr(1);
5137             }
5138
5139             while(q && lq != q){
5140                 lq = q;
5141                 var tm = q.match(tagTokenRe);
5142                 if(type == "select"){
5143                     if(tm){
5144                         if(tm[1] == "#"){
5145                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5146                         }else{
5147                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5148                         }
5149                         q = q.replace(tm[0], "");
5150                     }else if(q.substr(0, 1) != '@'){
5151                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5152                     }
5153                 }else{
5154                     if(tm){
5155                         if(tm[1] == "#"){
5156                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5157                         }else{
5158                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5159                         }
5160                         q = q.replace(tm[0], "");
5161                     }
5162                 }
5163                 while(!(mm = q.match(modeRe))){
5164                     var matched = false;
5165                     for(var j = 0; j < tklen; j++){
5166                         var t = tk[j];
5167                         var m = q.match(t.re);
5168                         if(m){
5169                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5170                                                     return m[i];
5171                                                 });
5172                             q = q.replace(m[0], "");
5173                             matched = true;
5174                             break;
5175                         }
5176                     }
5177                     // prevent infinite loop on bad selector
5178                     if(!matched){
5179                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5180                     }
5181                 }
5182                 if(mm[1]){
5183                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5184                     q = q.replace(mm[1], "");
5185                 }
5186             }
5187             fn[fn.length] = "return nodup(n);\n}";
5188             
5189              /** 
5190               * list of variables that need from compression as they are used by eval.
5191              *  eval:var:batch 
5192              *  eval:var:nodup
5193              *  eval:var:byTag
5194              *  eval:var:ById
5195              *  eval:var:getNodes
5196              *  eval:var:quickId
5197              *  eval:var:mode
5198              *  eval:var:root
5199              *  eval:var:n
5200              *  eval:var:byClassName
5201              *  eval:var:byPseudo
5202              *  eval:var:byAttribute
5203              *  eval:var:attrValue
5204              * 
5205              **/ 
5206             eval(fn.join(""));
5207             return f;
5208         },
5209
5210         /**
5211          * Selects a group of elements.
5212          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5213          * @param {Node} root (optional) The start of the query (defaults to document).
5214          * @return {Array}
5215          */
5216         select : function(path, root, type){
5217             if(!root || root == document){
5218                 root = document;
5219             }
5220             if(typeof root == "string"){
5221                 root = document.getElementById(root);
5222             }
5223             var paths = path.split(",");
5224             var results = [];
5225             for(var i = 0, len = paths.length; i < len; i++){
5226                 var p = paths[i].replace(trimRe, "");
5227                 if(!cache[p]){
5228                     cache[p] = Roo.DomQuery.compile(p);
5229                     if(!cache[p]){
5230                         throw p + " is not a valid selector";
5231                     }
5232                 }
5233                 var result = cache[p](root);
5234                 if(result && result != document){
5235                     results = results.concat(result);
5236                 }
5237             }
5238             if(paths.length > 1){
5239                 return nodup(results);
5240             }
5241             return results;
5242         },
5243
5244         /**
5245          * Selects a single element.
5246          * @param {String} selector The selector/xpath query
5247          * @param {Node} root (optional) The start of the query (defaults to document).
5248          * @return {Element}
5249          */
5250         selectNode : function(path, root){
5251             return Roo.DomQuery.select(path, root)[0];
5252         },
5253
5254         /**
5255          * Selects the value of a node, optionally replacing null with the defaultValue.
5256          * @param {String} selector The selector/xpath query
5257          * @param {Node} root (optional) The start of the query (defaults to document).
5258          * @param {String} defaultValue
5259          */
5260         selectValue : function(path, root, defaultValue){
5261             path = path.replace(trimRe, "");
5262             if(!valueCache[path]){
5263                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5264             }
5265             var n = valueCache[path](root);
5266             n = n[0] ? n[0] : n;
5267             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5268             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5269         },
5270
5271         /**
5272          * Selects the value of a node, parsing integers and floats.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @param {Number} defaultValue
5276          * @return {Number}
5277          */
5278         selectNumber : function(path, root, defaultValue){
5279             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5280             return parseFloat(v);
5281         },
5282
5283         /**
5284          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5285          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5286          * @param {String} selector The simple selector to test
5287          * @return {Boolean}
5288          */
5289         is : function(el, ss){
5290             if(typeof el == "string"){
5291                 el = document.getElementById(el);
5292             }
5293             var isArray = (el instanceof Array);
5294             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5295             return isArray ? (result.length == el.length) : (result.length > 0);
5296         },
5297
5298         /**
5299          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5300          * @param {Array} el An array of elements to filter
5301          * @param {String} selector The simple selector to test
5302          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5303          * the selector instead of the ones that match
5304          * @return {Array}
5305          */
5306         filter : function(els, ss, nonMatches){
5307             ss = ss.replace(trimRe, "");
5308             if(!simpleCache[ss]){
5309                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5310             }
5311             var result = simpleCache[ss](els);
5312             return nonMatches ? quickDiff(result, els) : result;
5313         },
5314
5315         /**
5316          * Collection of matching regular expressions and code snippets.
5317          */
5318         matchers : [{
5319                 re: /^\.([\w-]+)/,
5320                 select: 'n = byClassName(n, null, " {1} ");'
5321             }, {
5322                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5323                 select: 'n = byPseudo(n, "{1}", "{2}");'
5324             },{
5325                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5326                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5327             }, {
5328                 re: /^#([\w-]+)/,
5329                 select: 'n = byId(n, null, "{1}");'
5330             },{
5331                 re: /^@([\w-]+)/,
5332                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5333             }
5334         ],
5335
5336         /**
5337          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5338          * 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;.
5339          */
5340         operators : {
5341             "=" : function(a, v){
5342                 return a == v;
5343             },
5344             "!=" : function(a, v){
5345                 return a != v;
5346             },
5347             "^=" : function(a, v){
5348                 return a && a.substr(0, v.length) == v;
5349             },
5350             "$=" : function(a, v){
5351                 return a && a.substr(a.length-v.length) == v;
5352             },
5353             "*=" : function(a, v){
5354                 return a && a.indexOf(v) !== -1;
5355             },
5356             "%=" : function(a, v){
5357                 return (a % v) == 0;
5358             },
5359             "|=" : function(a, v){
5360                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5361             },
5362             "~=" : function(a, v){
5363                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5364             }
5365         },
5366
5367         /**
5368          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5369          * and the argument (if any) supplied in the selector.
5370          */
5371         pseudos : {
5372             "first-child" : function(c){
5373                 var r = [], ri = -1, n;
5374                 for(var i = 0, ci; ci = n = c[i]; i++){
5375                     while((n = n.previousSibling) && n.nodeType != 1);
5376                     if(!n){
5377                         r[++ri] = ci;
5378                     }
5379                 }
5380                 return r;
5381             },
5382
5383             "last-child" : function(c){
5384                 var r = [], ri = -1, n;
5385                 for(var i = 0, ci; ci = n = c[i]; i++){
5386                     while((n = n.nextSibling) && n.nodeType != 1);
5387                     if(!n){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "nth-child" : function(c, a) {
5395                 var r = [], ri = -1;
5396                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5397                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5398                 for(var i = 0, n; n = c[i]; i++){
5399                     var pn = n.parentNode;
5400                     if (batch != pn._batch) {
5401                         var j = 0;
5402                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5403                             if(cn.nodeType == 1){
5404                                cn.nodeIndex = ++j;
5405                             }
5406                         }
5407                         pn._batch = batch;
5408                     }
5409                     if (f == 1) {
5410                         if (l == 0 || n.nodeIndex == l){
5411                             r[++ri] = n;
5412                         }
5413                     } else if ((n.nodeIndex + l) % f == 0){
5414                         r[++ri] = n;
5415                     }
5416                 }
5417
5418                 return r;
5419             },
5420
5421             "only-child" : function(c){
5422                 var r = [], ri = -1;;
5423                 for(var i = 0, ci; ci = c[i]; i++){
5424                     if(!prev(ci) && !next(ci)){
5425                         r[++ri] = ci;
5426                     }
5427                 }
5428                 return r;
5429             },
5430
5431             "empty" : function(c){
5432                 var r = [], ri = -1;
5433                 for(var i = 0, ci; ci = c[i]; i++){
5434                     var cns = ci.childNodes, j = 0, cn, empty = true;
5435                     while(cn = cns[j]){
5436                         ++j;
5437                         if(cn.nodeType == 1 || cn.nodeType == 3){
5438                             empty = false;
5439                             break;
5440                         }
5441                     }
5442                     if(empty){
5443                         r[++ri] = ci;
5444                     }
5445                 }
5446                 return r;
5447             },
5448
5449             "contains" : function(c, v){
5450                 var r = [], ri = -1;
5451                 for(var i = 0, ci; ci = c[i]; i++){
5452                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5453                         r[++ri] = ci;
5454                     }
5455                 }
5456                 return r;
5457             },
5458
5459             "nodeValue" : function(c, v){
5460                 var r = [], ri = -1;
5461                 for(var i = 0, ci; ci = c[i]; i++){
5462                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             },
5468
5469             "checked" : function(c){
5470                 var r = [], ri = -1;
5471                 for(var i = 0, ci; ci = c[i]; i++){
5472                     if(ci.checked == true){
5473                         r[++ri] = ci;
5474                     }
5475                 }
5476                 return r;
5477             },
5478
5479             "not" : function(c, ss){
5480                 return Roo.DomQuery.filter(c, ss, true);
5481             },
5482
5483             "odd" : function(c){
5484                 return this["nth-child"](c, "odd");
5485             },
5486
5487             "even" : function(c){
5488                 return this["nth-child"](c, "even");
5489             },
5490
5491             "nth" : function(c, a){
5492                 return c[a-1] || [];
5493             },
5494
5495             "first" : function(c){
5496                 return c[0] || [];
5497             },
5498
5499             "last" : function(c){
5500                 return c[c.length-1] || [];
5501             },
5502
5503             "has" : function(c, ss){
5504                 var s = Roo.DomQuery.select;
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(s(ss, ci).length > 0){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "next" : function(c, ss){
5515                 var is = Roo.DomQuery.is;
5516                 var r = [], ri = -1;
5517                 for(var i = 0, ci; ci = c[i]; i++){
5518                     var n = next(ci);
5519                     if(n && is(n, ss)){
5520                         r[++ri] = ci;
5521                     }
5522                 }
5523                 return r;
5524             },
5525
5526             "prev" : function(c, ss){
5527                 var is = Roo.DomQuery.is;
5528                 var r = [], ri = -1;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     var n = prev(ci);
5531                     if(n && is(n, ss)){
5532                         r[++ri] = ci;
5533                     }
5534                 }
5535                 return r;
5536             }
5537         }
5538     };
5539 }();
5540
5541 /**
5542  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5543  * @param {String} path The selector/xpath query
5544  * @param {Node} root (optional) The start of the query (defaults to document).
5545  * @return {Array}
5546  * @member Roo
5547  * @method query
5548  */
5549 Roo.query = Roo.DomQuery.select;
5550 /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560
5561 /**
5562  * @class Roo.util.Observable
5563  * Base class that provides a common interface for publishing events. Subclasses are expected to
5564  * to have a property "events" with all the events defined.<br>
5565  * For example:
5566  * <pre><code>
5567  Employee = function(name){
5568     this.name = name;
5569     this.addEvents({
5570         "fired" : true,
5571         "quit" : true
5572     });
5573  }
5574  Roo.extend(Employee, Roo.util.Observable);
5575 </code></pre>
5576  * @param {Object} config properties to use (incuding events / listeners)
5577  */
5578
5579 Roo.util.Observable = function(cfg){
5580     
5581     cfg = cfg|| {};
5582     this.addEvents(cfg.events || {});
5583     if (cfg.events) {
5584         delete cfg.events; // make sure
5585     }
5586      
5587     Roo.apply(this, cfg);
5588     
5589     if(this.listeners){
5590         this.on(this.listeners);
5591         delete this.listeners;
5592     }
5593 };
5594 Roo.util.Observable.prototype = {
5595     /** 
5596  * @cfg {Object} listeners  list of events and functions to call for this object, 
5597  * For example :
5598  * <pre><code>
5599     listeners :  { 
5600        'click' : function(e) {
5601            ..... 
5602         } ,
5603         .... 
5604     } 
5605   </code></pre>
5606  */
5607     
5608     
5609     /**
5610      * Fires the specified event with the passed parameters (minus the event name).
5611      * @param {String} eventName
5612      * @param {Object...} args Variable number of parameters are passed to handlers
5613      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5614      */
5615     fireEvent : function(){
5616         var ce = this.events[arguments[0].toLowerCase()];
5617         if(typeof ce == "object"){
5618             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5619         }else{
5620             return true;
5621         }
5622     },
5623
5624     // private
5625     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5626
5627     /**
5628      * Appends an event handler to this component
5629      * @param {String}   eventName The type of event to listen for
5630      * @param {Function} handler The method the event invokes
5631      * @param {Object}   scope (optional) The scope in which to execute the handler
5632      * function. The handler function's "this" context.
5633      * @param {Object}   options (optional) An object containing handler configuration
5634      * properties. This may contain any of the following properties:<ul>
5635      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5636      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5637      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5638      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5639      * by the specified number of milliseconds. If the event fires again within that time, the original
5640      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5641      * </ul><br>
5642      * <p>
5643      * <b>Combining Options</b><br>
5644      * Using the options argument, it is possible to combine different types of listeners:<br>
5645      * <br>
5646      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5647                 <pre><code>
5648                 el.on('click', this.onClick, this, {
5649                         single: true,
5650                 delay: 100,
5651                 forumId: 4
5652                 });
5653                 </code></pre>
5654      * <p>
5655      * <b>Attaching multiple handlers in 1 call</b><br>
5656      * The method also allows for a single argument to be passed which is a config object containing properties
5657      * which specify multiple handlers.
5658      * <pre><code>
5659                 el.on({
5660                         'click': {
5661                         fn: this.onClick,
5662                         scope: this,
5663                         delay: 100
5664                 }, 
5665                 'mouseover': {
5666                         fn: this.onMouseOver,
5667                         scope: this
5668                 },
5669                 'mouseout': {
5670                         fn: this.onMouseOut,
5671                         scope: this
5672                 }
5673                 });
5674                 </code></pre>
5675      * <p>
5676      * Or a shorthand syntax which passes the same scope object to all handlers:
5677         <pre><code>
5678                 el.on({
5679                         'click': this.onClick,
5680                 'mouseover': this.onMouseOver,
5681                 'mouseout': this.onMouseOut,
5682                 scope: this
5683                 });
5684                 </code></pre>
5685      */
5686     addListener : function(eventName, fn, scope, o){
5687         if(typeof eventName == "object"){
5688             o = eventName;
5689             for(var e in o){
5690                 if(this.filterOptRe.test(e)){
5691                     continue;
5692                 }
5693                 if(typeof o[e] == "function"){
5694                     // shared options
5695                     this.addListener(e, o[e], o.scope,  o);
5696                 }else{
5697                     // individual options
5698                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5699                 }
5700             }
5701             return;
5702         }
5703         o = (!o || typeof o == "boolean") ? {} : o;
5704         eventName = eventName.toLowerCase();
5705         var ce = this.events[eventName] || true;
5706         if(typeof ce == "boolean"){
5707             ce = new Roo.util.Event(this, eventName);
5708             this.events[eventName] = ce;
5709         }
5710         ce.addListener(fn, scope, o);
5711     },
5712
5713     /**
5714      * Removes a listener
5715      * @param {String}   eventName     The type of event to listen for
5716      * @param {Function} handler        The handler to remove
5717      * @param {Object}   scope  (optional) The scope (this object) for the handler
5718      */
5719     removeListener : function(eventName, fn, scope){
5720         var ce = this.events[eventName.toLowerCase()];
5721         if(typeof ce == "object"){
5722             ce.removeListener(fn, scope);
5723         }
5724     },
5725
5726     /**
5727      * Removes all listeners for this object
5728      */
5729     purgeListeners : function(){
5730         for(var evt in this.events){
5731             if(typeof this.events[evt] == "object"){
5732                  this.events[evt].clearListeners();
5733             }
5734         }
5735     },
5736
5737     relayEvents : function(o, events){
5738         var createHandler = function(ename){
5739             return function(){
5740                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5741             };
5742         };
5743         for(var i = 0, len = events.length; i < len; i++){
5744             var ename = events[i];
5745             if(!this.events[ename]){ this.events[ename] = true; };
5746             o.on(ename, createHandler(ename), this);
5747         }
5748     },
5749
5750     /**
5751      * Used to define events on this Observable
5752      * @param {Object} object The object with the events defined
5753      */
5754     addEvents : function(o){
5755         if(!this.events){
5756             this.events = {};
5757         }
5758         Roo.applyIf(this.events, o);
5759     },
5760
5761     /**
5762      * Checks to see if this object has any listeners for a specified event
5763      * @param {String} eventName The name of the event to check for
5764      * @return {Boolean} True if the event is being listened for, else false
5765      */
5766     hasListener : function(eventName){
5767         var e = this.events[eventName];
5768         return typeof e == "object" && e.listeners.length > 0;
5769     }
5770 };
5771 /**
5772  * Appends an event handler to this element (shorthand for addListener)
5773  * @param {String}   eventName     The type of event to listen for
5774  * @param {Function} handler        The method the event invokes
5775  * @param {Object}   scope (optional) The scope in which to execute the handler
5776  * function. The handler function's "this" context.
5777  * @param {Object}   options  (optional)
5778  * @method
5779  */
5780 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5781 /**
5782  * Removes a listener (shorthand for removeListener)
5783  * @param {String}   eventName     The type of event to listen for
5784  * @param {Function} handler        The handler to remove
5785  * @param {Object}   scope  (optional) The scope (this object) for the handler
5786  * @method
5787  */
5788 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5789
5790 /**
5791  * Starts capture on the specified Observable. All events will be passed
5792  * to the supplied function with the event name + standard signature of the event
5793  * <b>before</b> the event is fired. If the supplied function returns false,
5794  * the event will not fire.
5795  * @param {Observable} o The Observable to capture
5796  * @param {Function} fn The function to call
5797  * @param {Object} scope (optional) The scope (this object) for the fn
5798  * @static
5799  */
5800 Roo.util.Observable.capture = function(o, fn, scope){
5801     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5802 };
5803
5804 /**
5805  * Removes <b>all</b> added captures from the Observable.
5806  * @param {Observable} o The Observable to release
5807  * @static
5808  */
5809 Roo.util.Observable.releaseCapture = function(o){
5810     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5811 };
5812
5813 (function(){
5814
5815     var createBuffered = function(h, o, scope){
5816         var task = new Roo.util.DelayedTask();
5817         return function(){
5818             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5819         };
5820     };
5821
5822     var createSingle = function(h, e, fn, scope){
5823         return function(){
5824             e.removeListener(fn, scope);
5825             return h.apply(scope, arguments);
5826         };
5827     };
5828
5829     var createDelayed = function(h, o, scope){
5830         return function(){
5831             var args = Array.prototype.slice.call(arguments, 0);
5832             setTimeout(function(){
5833                 h.apply(scope, args);
5834             }, o.delay || 10);
5835         };
5836     };
5837
5838     Roo.util.Event = function(obj, name){
5839         this.name = name;
5840         this.obj = obj;
5841         this.listeners = [];
5842     };
5843
5844     Roo.util.Event.prototype = {
5845         addListener : function(fn, scope, options){
5846             var o = options || {};
5847             scope = scope || this.obj;
5848             if(!this.isListening(fn, scope)){
5849                 var l = {fn: fn, scope: scope, options: o};
5850                 var h = fn;
5851                 if(o.delay){
5852                     h = createDelayed(h, o, scope);
5853                 }
5854                 if(o.single){
5855                     h = createSingle(h, this, fn, scope);
5856                 }
5857                 if(o.buffer){
5858                     h = createBuffered(h, o, scope);
5859                 }
5860                 l.fireFn = h;
5861                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5862                     this.listeners.push(l);
5863                 }else{
5864                     this.listeners = this.listeners.slice(0);
5865                     this.listeners.push(l);
5866                 }
5867             }
5868         },
5869
5870         findListener : function(fn, scope){
5871             scope = scope || this.obj;
5872             var ls = this.listeners;
5873             for(var i = 0, len = ls.length; i < len; i++){
5874                 var l = ls[i];
5875                 if(l.fn == fn && l.scope == scope){
5876                     return i;
5877                 }
5878             }
5879             return -1;
5880         },
5881
5882         isListening : function(fn, scope){
5883             return this.findListener(fn, scope) != -1;
5884         },
5885
5886         removeListener : function(fn, scope){
5887             var index;
5888             if((index = this.findListener(fn, scope)) != -1){
5889                 if(!this.firing){
5890                     this.listeners.splice(index, 1);
5891                 }else{
5892                     this.listeners = this.listeners.slice(0);
5893                     this.listeners.splice(index, 1);
5894                 }
5895                 return true;
5896             }
5897             return false;
5898         },
5899
5900         clearListeners : function(){
5901             this.listeners = [];
5902         },
5903
5904         fire : function(){
5905             var ls = this.listeners, scope, len = ls.length;
5906             if(len > 0){
5907                 this.firing = true;
5908                 var args = Array.prototype.slice.call(arguments, 0);
5909                 for(var i = 0; i < len; i++){
5910                     var l = ls[i];
5911                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5912                         this.firing = false;
5913                         return false;
5914                     }
5915                 }
5916                 this.firing = false;
5917             }
5918             return true;
5919         }
5920     };
5921 })();/*
5922  * Based on:
5923  * Ext JS Library 1.1.1
5924  * Copyright(c) 2006-2007, Ext JS, LLC.
5925  *
5926  * Originally Released Under LGPL - original licence link has changed is not relivant.
5927  *
5928  * Fork - LGPL
5929  * <script type="text/javascript">
5930  */
5931
5932 /**
5933  * @class Roo.EventManager
5934  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5935  * several useful events directly.
5936  * See {@link Roo.EventObject} for more details on normalized event objects.
5937  * @singleton
5938  */
5939 Roo.EventManager = function(){
5940     var docReadyEvent, docReadyProcId, docReadyState = false;
5941     var resizeEvent, resizeTask, textEvent, textSize;
5942     var E = Roo.lib.Event;
5943     var D = Roo.lib.Dom;
5944
5945
5946     var fireDocReady = function(){
5947         if(!docReadyState){
5948             docReadyState = true;
5949             Roo.isReady = true;
5950             if(docReadyProcId){
5951                 clearInterval(docReadyProcId);
5952             }
5953             if(Roo.isGecko || Roo.isOpera) {
5954                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5955             }
5956             if(Roo.isIE){
5957                 var defer = document.getElementById("ie-deferred-loader");
5958                 if(defer){
5959                     defer.onreadystatechange = null;
5960                     defer.parentNode.removeChild(defer);
5961                 }
5962             }
5963             if(docReadyEvent){
5964                 docReadyEvent.fire();
5965                 docReadyEvent.clearListeners();
5966             }
5967         }
5968     };
5969     
5970     var initDocReady = function(){
5971         docReadyEvent = new Roo.util.Event();
5972         if(Roo.isGecko || Roo.isOpera) {
5973             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5974         }else if(Roo.isIE){
5975             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5976             var defer = document.getElementById("ie-deferred-loader");
5977             defer.onreadystatechange = function(){
5978                 if(this.readyState == "complete"){
5979                     fireDocReady();
5980                 }
5981             };
5982         }else if(Roo.isSafari){ 
5983             docReadyProcId = setInterval(function(){
5984                 var rs = document.readyState;
5985                 if(rs == "complete") {
5986                     fireDocReady();     
5987                  }
5988             }, 10);
5989         }
5990         // no matter what, make sure it fires on load
5991         E.on(window, "load", fireDocReady);
5992     };
5993
5994     var createBuffered = function(h, o){
5995         var task = new Roo.util.DelayedTask(h);
5996         return function(e){
5997             // create new event object impl so new events don't wipe out properties
5998             e = new Roo.EventObjectImpl(e);
5999             task.delay(o.buffer, h, null, [e]);
6000         };
6001     };
6002
6003     var createSingle = function(h, el, ename, fn){
6004         return function(e){
6005             Roo.EventManager.removeListener(el, ename, fn);
6006             h(e);
6007         };
6008     };
6009
6010     var createDelayed = function(h, o){
6011         return function(e){
6012             // create new event object impl so new events don't wipe out properties
6013             e = new Roo.EventObjectImpl(e);
6014             setTimeout(function(){
6015                 h(e);
6016             }, o.delay || 10);
6017         };
6018     };
6019
6020     var listen = function(element, ename, opt, fn, scope){
6021         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6022         fn = fn || o.fn; scope = scope || o.scope;
6023         var el = Roo.getDom(element);
6024         if(!el){
6025             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6026         }
6027         var h = function(e){
6028             e = Roo.EventObject.setEvent(e);
6029             var t;
6030             if(o.delegate){
6031                 t = e.getTarget(o.delegate, el);
6032                 if(!t){
6033                     return;
6034                 }
6035             }else{
6036                 t = e.target;
6037             }
6038             if(o.stopEvent === true){
6039                 e.stopEvent();
6040             }
6041             if(o.preventDefault === true){
6042                e.preventDefault();
6043             }
6044             if(o.stopPropagation === true){
6045                 e.stopPropagation();
6046             }
6047
6048             if(o.normalized === false){
6049                 e = e.browserEvent;
6050             }
6051
6052             fn.call(scope || el, e, t, o);
6053         };
6054         if(o.delay){
6055             h = createDelayed(h, o);
6056         }
6057         if(o.single){
6058             h = createSingle(h, el, ename, fn);
6059         }
6060         if(o.buffer){
6061             h = createBuffered(h, o);
6062         }
6063         fn._handlers = fn._handlers || [];
6064         fn._handlers.push([Roo.id(el), ename, h]);
6065
6066         E.on(el, ename, h);
6067         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6068             el.addEventListener("DOMMouseScroll", h, false);
6069             E.on(window, 'unload', function(){
6070                 el.removeEventListener("DOMMouseScroll", h, false);
6071             });
6072         }
6073         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6074             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6075         }
6076         return h;
6077     };
6078
6079     var stopListening = function(el, ename, fn){
6080         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6081         if(hds){
6082             for(var i = 0, len = hds.length; i < len; i++){
6083                 var h = hds[i];
6084                 if(h[0] == id && h[1] == ename){
6085                     hd = h[2];
6086                     hds.splice(i, 1);
6087                     break;
6088                 }
6089             }
6090         }
6091         E.un(el, ename, hd);
6092         el = Roo.getDom(el);
6093         if(ename == "mousewheel" && el.addEventListener){
6094             el.removeEventListener("DOMMouseScroll", hd, false);
6095         }
6096         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6097             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6098         }
6099     };
6100
6101     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6102     
6103     var pub = {
6104         
6105         
6106         /** 
6107          * Fix for doc tools
6108          * @scope Roo.EventManager
6109          */
6110         
6111         
6112         /** 
6113          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6114          * object with a Roo.EventObject
6115          * @param {Function} fn        The method the event invokes
6116          * @param {Object}   scope    An object that becomes the scope of the handler
6117          * @param {boolean}  override If true, the obj passed in becomes
6118          *                             the execution scope of the listener
6119          * @return {Function} The wrapped function
6120          * @deprecated
6121          */
6122         wrap : function(fn, scope, override){
6123             return function(e){
6124                 Roo.EventObject.setEvent(e);
6125                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6126             };
6127         },
6128         
6129         /**
6130      * Appends an event handler to an element (shorthand for addListener)
6131      * @param {String/HTMLElement}   element        The html element or id to assign the
6132      * @param {String}   eventName The type of event to listen for
6133      * @param {Function} handler The method the event invokes
6134      * @param {Object}   scope (optional) The scope in which to execute the handler
6135      * function. The handler function's "this" context.
6136      * @param {Object}   options (optional) An object containing handler configuration
6137      * properties. This may contain any of the following properties:<ul>
6138      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6139      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6140      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6141      * <li>preventDefault {Boolean} True to prevent the default action</li>
6142      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6143      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6144      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6145      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6146      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6147      * by the specified number of milliseconds. If the event fires again within that time, the original
6148      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6149      * </ul><br>
6150      * <p>
6151      * <b>Combining Options</b><br>
6152      * Using the options argument, it is possible to combine different types of listeners:<br>
6153      * <br>
6154      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6155      * Code:<pre><code>
6156 el.on('click', this.onClick, this, {
6157     single: true,
6158     delay: 100,
6159     stopEvent : true,
6160     forumId: 4
6161 });</code></pre>
6162      * <p>
6163      * <b>Attaching multiple handlers in 1 call</b><br>
6164       * The method also allows for a single argument to be passed which is a config object containing properties
6165      * which specify multiple handlers.
6166      * <p>
6167      * Code:<pre><code>
6168 el.on({
6169     'click' : {
6170         fn: this.onClick
6171         scope: this,
6172         delay: 100
6173     },
6174     'mouseover' : {
6175         fn: this.onMouseOver
6176         scope: this
6177     },
6178     'mouseout' : {
6179         fn: this.onMouseOut
6180         scope: this
6181     }
6182 });</code></pre>
6183      * <p>
6184      * Or a shorthand syntax:<br>
6185      * Code:<pre><code>
6186 el.on({
6187     'click' : this.onClick,
6188     'mouseover' : this.onMouseOver,
6189     'mouseout' : this.onMouseOut
6190     scope: this
6191 });</code></pre>
6192      */
6193         addListener : function(element, eventName, fn, scope, options){
6194             if(typeof eventName == "object"){
6195                 var o = eventName;
6196                 for(var e in o){
6197                     if(propRe.test(e)){
6198                         continue;
6199                     }
6200                     if(typeof o[e] == "function"){
6201                         // shared options
6202                         listen(element, e, o, o[e], o.scope);
6203                     }else{
6204                         // individual options
6205                         listen(element, e, o[e]);
6206                     }
6207                 }
6208                 return;
6209             }
6210             return listen(element, eventName, options, fn, scope);
6211         },
6212         
6213         /**
6214          * Removes an event handler
6215          *
6216          * @param {String/HTMLElement}   element        The id or html element to remove the 
6217          *                             event from
6218          * @param {String}   eventName     The type of event
6219          * @param {Function} fn
6220          * @return {Boolean} True if a listener was actually removed
6221          */
6222         removeListener : function(element, eventName, fn){
6223             return stopListening(element, eventName, fn);
6224         },
6225         
6226         /**
6227          * Fires when the document is ready (before onload and before images are loaded). Can be 
6228          * accessed shorthanded Roo.onReady().
6229          * @param {Function} fn        The method the event invokes
6230          * @param {Object}   scope    An  object that becomes the scope of the handler
6231          * @param {boolean}  options
6232          */
6233         onDocumentReady : function(fn, scope, options){
6234             if(docReadyState){ // if it already fired
6235                 docReadyEvent.addListener(fn, scope, options);
6236                 docReadyEvent.fire();
6237                 docReadyEvent.clearListeners();
6238                 return;
6239             }
6240             if(!docReadyEvent){
6241                 initDocReady();
6242             }
6243             docReadyEvent.addListener(fn, scope, options);
6244         },
6245         
6246         /**
6247          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6248          * @param {Function} fn        The method the event invokes
6249          * @param {Object}   scope    An object that becomes the scope of the handler
6250          * @param {boolean}  options
6251          */
6252         onWindowResize : function(fn, scope, options){
6253             if(!resizeEvent){
6254                 resizeEvent = new Roo.util.Event();
6255                 resizeTask = new Roo.util.DelayedTask(function(){
6256                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6257                 });
6258                 E.on(window, "resize", function(){
6259                     if(Roo.isIE){
6260                         resizeTask.delay(50);
6261                     }else{
6262                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6263                     }
6264                 });
6265             }
6266             resizeEvent.addListener(fn, scope, options);
6267         },
6268
6269         /**
6270          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6271          * @param {Function} fn        The method the event invokes
6272          * @param {Object}   scope    An object that becomes the scope of the handler
6273          * @param {boolean}  options
6274          */
6275         onTextResize : function(fn, scope, options){
6276             if(!textEvent){
6277                 textEvent = new Roo.util.Event();
6278                 var textEl = new Roo.Element(document.createElement('div'));
6279                 textEl.dom.className = 'x-text-resize';
6280                 textEl.dom.innerHTML = 'X';
6281                 textEl.appendTo(document.body);
6282                 textSize = textEl.dom.offsetHeight;
6283                 setInterval(function(){
6284                     if(textEl.dom.offsetHeight != textSize){
6285                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6286                     }
6287                 }, this.textResizeInterval);
6288             }
6289             textEvent.addListener(fn, scope, options);
6290         },
6291
6292         /**
6293          * Removes the passed window resize listener.
6294          * @param {Function} fn        The method the event invokes
6295          * @param {Object}   scope    The scope of handler
6296          */
6297         removeResizeListener : function(fn, scope){
6298             if(resizeEvent){
6299                 resizeEvent.removeListener(fn, scope);
6300             }
6301         },
6302
6303         // private
6304         fireResize : function(){
6305             if(resizeEvent){
6306                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6307             }   
6308         },
6309         /**
6310          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6311          */
6312         ieDeferSrc : false,
6313         /**
6314          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6315          */
6316         textResizeInterval : 50
6317     };
6318     
6319     /**
6320      * Fix for doc tools
6321      * @scopeAlias pub=Roo.EventManager
6322      */
6323     
6324      /**
6325      * Appends an event handler to an element (shorthand for addListener)
6326      * @param {String/HTMLElement}   element        The html element or id to assign the
6327      * @param {String}   eventName The type of event to listen for
6328      * @param {Function} handler The method the event invokes
6329      * @param {Object}   scope (optional) The scope in which to execute the handler
6330      * function. The handler function's "this" context.
6331      * @param {Object}   options (optional) An object containing handler configuration
6332      * properties. This may contain any of the following properties:<ul>
6333      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6334      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6335      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6336      * <li>preventDefault {Boolean} True to prevent the default action</li>
6337      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6338      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6339      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6340      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6341      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6342      * by the specified number of milliseconds. If the event fires again within that time, the original
6343      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6344      * </ul><br>
6345      * <p>
6346      * <b>Combining Options</b><br>
6347      * Using the options argument, it is possible to combine different types of listeners:<br>
6348      * <br>
6349      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6350      * Code:<pre><code>
6351 el.on('click', this.onClick, this, {
6352     single: true,
6353     delay: 100,
6354     stopEvent : true,
6355     forumId: 4
6356 });</code></pre>
6357      * <p>
6358      * <b>Attaching multiple handlers in 1 call</b><br>
6359       * The method also allows for a single argument to be passed which is a config object containing properties
6360      * which specify multiple handlers.
6361      * <p>
6362      * Code:<pre><code>
6363 el.on({
6364     'click' : {
6365         fn: this.onClick
6366         scope: this,
6367         delay: 100
6368     },
6369     'mouseover' : {
6370         fn: this.onMouseOver
6371         scope: this
6372     },
6373     'mouseout' : {
6374         fn: this.onMouseOut
6375         scope: this
6376     }
6377 });</code></pre>
6378      * <p>
6379      * Or a shorthand syntax:<br>
6380      * Code:<pre><code>
6381 el.on({
6382     'click' : this.onClick,
6383     'mouseover' : this.onMouseOver,
6384     'mouseout' : this.onMouseOut
6385     scope: this
6386 });</code></pre>
6387      */
6388     pub.on = pub.addListener;
6389     pub.un = pub.removeListener;
6390
6391     pub.stoppedMouseDownEvent = new Roo.util.Event();
6392     return pub;
6393 }();
6394 /**
6395   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6396   * @param {Function} fn        The method the event invokes
6397   * @param {Object}   scope    An  object that becomes the scope of the handler
6398   * @param {boolean}  override If true, the obj passed in becomes
6399   *                             the execution scope of the listener
6400   * @member Roo
6401   * @method onReady
6402  */
6403 Roo.onReady = Roo.EventManager.onDocumentReady;
6404
6405 Roo.onReady(function(){
6406     var bd = Roo.get(document.body);
6407     if(!bd){ return; }
6408
6409     var cls = [
6410             Roo.isIE ? "roo-ie"
6411             : Roo.isGecko ? "roo-gecko"
6412             : Roo.isOpera ? "roo-opera"
6413             : Roo.isSafari ? "roo-safari" : ""];
6414
6415     if(Roo.isMac){
6416         cls.push("roo-mac");
6417     }
6418     if(Roo.isLinux){
6419         cls.push("roo-linux");
6420     }
6421     if(Roo.isBorderBox){
6422         cls.push('roo-border-box');
6423     }
6424     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6425         var p = bd.dom.parentNode;
6426         if(p){
6427             p.className += ' roo-strict';
6428         }
6429     }
6430     bd.addClass(cls.join(' '));
6431 });
6432
6433 /**
6434  * @class Roo.EventObject
6435  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6436  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6437  * Example:
6438  * <pre><code>
6439  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6440     e.preventDefault();
6441     var target = e.getTarget();
6442     ...
6443  }
6444  var myDiv = Roo.get("myDiv");
6445  myDiv.on("click", handleClick);
6446  //or
6447  Roo.EventManager.on("myDiv", 'click', handleClick);
6448  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6449  </code></pre>
6450  * @singleton
6451  */
6452 Roo.EventObject = function(){
6453     
6454     var E = Roo.lib.Event;
6455     
6456     // safari keypress events for special keys return bad keycodes
6457     var safariKeys = {
6458         63234 : 37, // left
6459         63235 : 39, // right
6460         63232 : 38, // up
6461         63233 : 40, // down
6462         63276 : 33, // page up
6463         63277 : 34, // page down
6464         63272 : 46, // delete
6465         63273 : 36, // home
6466         63275 : 35  // end
6467     };
6468
6469     // normalize button clicks
6470     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6471                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6472
6473     Roo.EventObjectImpl = function(e){
6474         if(e){
6475             this.setEvent(e.browserEvent || e);
6476         }
6477     };
6478     Roo.EventObjectImpl.prototype = {
6479         /**
6480          * Used to fix doc tools.
6481          * @scope Roo.EventObject.prototype
6482          */
6483             
6484
6485         
6486         
6487         /** The normal browser event */
6488         browserEvent : null,
6489         /** The button pressed in a mouse event */
6490         button : -1,
6491         /** True if the shift key was down during the event */
6492         shiftKey : false,
6493         /** True if the control key was down during the event */
6494         ctrlKey : false,
6495         /** True if the alt key was down during the event */
6496         altKey : false,
6497
6498         /** Key constant 
6499         * @type Number */
6500         BACKSPACE : 8,
6501         /** Key constant 
6502         * @type Number */
6503         TAB : 9,
6504         /** Key constant 
6505         * @type Number */
6506         RETURN : 13,
6507         /** Key constant 
6508         * @type Number */
6509         ENTER : 13,
6510         /** Key constant 
6511         * @type Number */
6512         SHIFT : 16,
6513         /** Key constant 
6514         * @type Number */
6515         CONTROL : 17,
6516         /** Key constant 
6517         * @type Number */
6518         ESC : 27,
6519         /** Key constant 
6520         * @type Number */
6521         SPACE : 32,
6522         /** Key constant 
6523         * @type Number */
6524         PAGEUP : 33,
6525         /** Key constant 
6526         * @type Number */
6527         PAGEDOWN : 34,
6528         /** Key constant 
6529         * @type Number */
6530         END : 35,
6531         /** Key constant 
6532         * @type Number */
6533         HOME : 36,
6534         /** Key constant 
6535         * @type Number */
6536         LEFT : 37,
6537         /** Key constant 
6538         * @type Number */
6539         UP : 38,
6540         /** Key constant 
6541         * @type Number */
6542         RIGHT : 39,
6543         /** Key constant 
6544         * @type Number */
6545         DOWN : 40,
6546         /** Key constant 
6547         * @type Number */
6548         DELETE : 46,
6549         /** Key constant 
6550         * @type Number */
6551         F5 : 116,
6552
6553            /** @private */
6554         setEvent : function(e){
6555             if(e == this || (e && e.browserEvent)){ // already wrapped
6556                 return e;
6557             }
6558             this.browserEvent = e;
6559             if(e){
6560                 // normalize buttons
6561                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6562                 if(e.type == 'click' && this.button == -1){
6563                     this.button = 0;
6564                 }
6565                 this.type = e.type;
6566                 this.shiftKey = e.shiftKey;
6567                 // mac metaKey behaves like ctrlKey
6568                 this.ctrlKey = e.ctrlKey || e.metaKey;
6569                 this.altKey = e.altKey;
6570                 // in getKey these will be normalized for the mac
6571                 this.keyCode = e.keyCode;
6572                 // keyup warnings on firefox.
6573                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6574                 // cache the target for the delayed and or buffered events
6575                 this.target = E.getTarget(e);
6576                 // same for XY
6577                 this.xy = E.getXY(e);
6578             }else{
6579                 this.button = -1;
6580                 this.shiftKey = false;
6581                 this.ctrlKey = false;
6582                 this.altKey = false;
6583                 this.keyCode = 0;
6584                 this.charCode =0;
6585                 this.target = null;
6586                 this.xy = [0, 0];
6587             }
6588             return this;
6589         },
6590
6591         /**
6592          * Stop the event (preventDefault and stopPropagation)
6593          */
6594         stopEvent : function(){
6595             if(this.browserEvent){
6596                 if(this.browserEvent.type == 'mousedown'){
6597                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6598                 }
6599                 E.stopEvent(this.browserEvent);
6600             }
6601         },
6602
6603         /**
6604          * Prevents the browsers default handling of the event.
6605          */
6606         preventDefault : function(){
6607             if(this.browserEvent){
6608                 E.preventDefault(this.browserEvent);
6609             }
6610         },
6611
6612         /** @private */
6613         isNavKeyPress : function(){
6614             var k = this.keyCode;
6615             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6616             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6617         },
6618
6619         isSpecialKey : function(){
6620             var k = this.keyCode;
6621             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6622             (k == 16) || (k == 17) ||
6623             (k >= 18 && k <= 20) ||
6624             (k >= 33 && k <= 35) ||
6625             (k >= 36 && k <= 39) ||
6626             (k >= 44 && k <= 45);
6627         },
6628         /**
6629          * Cancels bubbling of the event.
6630          */
6631         stopPropagation : function(){
6632             if(this.browserEvent){
6633                 if(this.type == 'mousedown'){
6634                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6635                 }
6636                 E.stopPropagation(this.browserEvent);
6637             }
6638         },
6639
6640         /**
6641          * Gets the key code for the event.
6642          * @return {Number}
6643          */
6644         getCharCode : function(){
6645             return this.charCode || this.keyCode;
6646         },
6647
6648         /**
6649          * Returns a normalized keyCode for the event.
6650          * @return {Number} The key code
6651          */
6652         getKey : function(){
6653             var k = this.keyCode || this.charCode;
6654             return Roo.isSafari ? (safariKeys[k] || k) : k;
6655         },
6656
6657         /**
6658          * Gets the x coordinate of the event.
6659          * @return {Number}
6660          */
6661         getPageX : function(){
6662             return this.xy[0];
6663         },
6664
6665         /**
6666          * Gets the y coordinate of the event.
6667          * @return {Number}
6668          */
6669         getPageY : function(){
6670             return this.xy[1];
6671         },
6672
6673         /**
6674          * Gets the time of the event.
6675          * @return {Number}
6676          */
6677         getTime : function(){
6678             if(this.browserEvent){
6679                 return E.getTime(this.browserEvent);
6680             }
6681             return null;
6682         },
6683
6684         /**
6685          * Gets the page coordinates of the event.
6686          * @return {Array} The xy values like [x, y]
6687          */
6688         getXY : function(){
6689             return this.xy;
6690         },
6691
6692         /**
6693          * Gets the target for the event.
6694          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6695          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6696                 search as a number or element (defaults to 10 || document.body)
6697          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6698          * @return {HTMLelement}
6699          */
6700         getTarget : function(selector, maxDepth, returnEl){
6701             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6702         },
6703         /**
6704          * Gets the related target.
6705          * @return {HTMLElement}
6706          */
6707         getRelatedTarget : function(){
6708             if(this.browserEvent){
6709                 return E.getRelatedTarget(this.browserEvent);
6710             }
6711             return null;
6712         },
6713
6714         /**
6715          * Normalizes mouse wheel delta across browsers
6716          * @return {Number} The delta
6717          */
6718         getWheelDelta : function(){
6719             var e = this.browserEvent;
6720             var delta = 0;
6721             if(e.wheelDelta){ /* IE/Opera. */
6722                 delta = e.wheelDelta/120;
6723             }else if(e.detail){ /* Mozilla case. */
6724                 delta = -e.detail/3;
6725             }
6726             return delta;
6727         },
6728
6729         /**
6730          * Returns true if the control, meta, shift or alt key was pressed during this event.
6731          * @return {Boolean}
6732          */
6733         hasModifier : function(){
6734             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6735         },
6736
6737         /**
6738          * Returns true if the target of this event equals el or is a child of el
6739          * @param {String/HTMLElement/Element} el
6740          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6741          * @return {Boolean}
6742          */
6743         within : function(el, related){
6744             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6745             return t && Roo.fly(el).contains(t);
6746         },
6747
6748         getPoint : function(){
6749             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6750         }
6751     };
6752
6753     return new Roo.EventObjectImpl();
6754 }();
6755             
6756     /*
6757  * Based on:
6758  * Ext JS Library 1.1.1
6759  * Copyright(c) 2006-2007, Ext JS, LLC.
6760  *
6761  * Originally Released Under LGPL - original licence link has changed is not relivant.
6762  *
6763  * Fork - LGPL
6764  * <script type="text/javascript">
6765  */
6766
6767  
6768 // was in Composite Element!??!?!
6769  
6770 (function(){
6771     var D = Roo.lib.Dom;
6772     var E = Roo.lib.Event;
6773     var A = Roo.lib.Anim;
6774
6775     // local style camelizing for speed
6776     var propCache = {};
6777     var camelRe = /(-[a-z])/gi;
6778     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6779     var view = document.defaultView;
6780
6781 /**
6782  * @class Roo.Element
6783  * Represents an Element in the DOM.<br><br>
6784  * Usage:<br>
6785 <pre><code>
6786 var el = Roo.get("my-div");
6787
6788 // or with getEl
6789 var el = getEl("my-div");
6790
6791 // or with a DOM element
6792 var el = Roo.get(myDivElement);
6793 </code></pre>
6794  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6795  * each call instead of constructing a new one.<br><br>
6796  * <b>Animations</b><br />
6797  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6798  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6799 <pre>
6800 Option    Default   Description
6801 --------- --------  ---------------------------------------------
6802 duration  .35       The duration of the animation in seconds
6803 easing    easeOut   The YUI easing method
6804 callback  none      A function to execute when the anim completes
6805 scope     this      The scope (this) of the callback function
6806 </pre>
6807 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6808 * manipulate the animation. Here's an example:
6809 <pre><code>
6810 var el = Roo.get("my-div");
6811
6812 // no animation
6813 el.setWidth(100);
6814
6815 // default animation
6816 el.setWidth(100, true);
6817
6818 // animation with some options set
6819 el.setWidth(100, {
6820     duration: 1,
6821     callback: this.foo,
6822     scope: this
6823 });
6824
6825 // using the "anim" property to get the Anim object
6826 var opt = {
6827     duration: 1,
6828     callback: this.foo,
6829     scope: this
6830 };
6831 el.setWidth(100, opt);
6832 ...
6833 if(opt.anim.isAnimated()){
6834     opt.anim.stop();
6835 }
6836 </code></pre>
6837 * <b> Composite (Collections of) Elements</b><br />
6838  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6839  * @constructor Create a new Element directly.
6840  * @param {String/HTMLElement} element
6841  * @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).
6842  */
6843     Roo.Element = function(element, forceNew){
6844         var dom = typeof element == "string" ?
6845                 document.getElementById(element) : element;
6846         if(!dom){ // invalid id/element
6847             return null;
6848         }
6849         var id = dom.id;
6850         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6851             return Roo.Element.cache[id];
6852         }
6853
6854         /**
6855          * The DOM element
6856          * @type HTMLElement
6857          */
6858         this.dom = dom;
6859
6860         /**
6861          * The DOM element ID
6862          * @type String
6863          */
6864         this.id = id || Roo.id(dom);
6865     };
6866
6867     var El = Roo.Element;
6868
6869     El.prototype = {
6870         /**
6871          * The element's default display mode  (defaults to "")
6872          * @type String
6873          */
6874         originalDisplay : "",
6875
6876         visibilityMode : 1,
6877         /**
6878          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6879          * @type String
6880          */
6881         defaultUnit : "px",
6882         /**
6883          * Sets the element's visibility mode. When setVisible() is called it
6884          * will use this to determine whether to set the visibility or the display property.
6885          * @param visMode Element.VISIBILITY or Element.DISPLAY
6886          * @return {Roo.Element} this
6887          */
6888         setVisibilityMode : function(visMode){
6889             this.visibilityMode = visMode;
6890             return this;
6891         },
6892         /**
6893          * Convenience method for setVisibilityMode(Element.DISPLAY)
6894          * @param {String} display (optional) What to set display to when visible
6895          * @return {Roo.Element} this
6896          */
6897         enableDisplayMode : function(display){
6898             this.setVisibilityMode(El.DISPLAY);
6899             if(typeof display != "undefined") this.originalDisplay = display;
6900             return this;
6901         },
6902
6903         /**
6904          * 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)
6905          * @param {String} selector The simple selector to test
6906          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6907                 search as a number or element (defaults to 10 || document.body)
6908          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6909          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6910          */
6911         findParent : function(simpleSelector, maxDepth, returnEl){
6912             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6913             maxDepth = maxDepth || 50;
6914             if(typeof maxDepth != "number"){
6915                 stopEl = Roo.getDom(maxDepth);
6916                 maxDepth = 10;
6917             }
6918             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6919                 if(dq.is(p, simpleSelector)){
6920                     return returnEl ? Roo.get(p) : p;
6921                 }
6922                 depth++;
6923                 p = p.parentNode;
6924             }
6925             return null;
6926         },
6927
6928
6929         /**
6930          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6931          * @param {String} selector The simple selector to test
6932          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6933                 search as a number or element (defaults to 10 || document.body)
6934          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6935          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6936          */
6937         findParentNode : function(simpleSelector, maxDepth, returnEl){
6938             var p = Roo.fly(this.dom.parentNode, '_internal');
6939             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6940         },
6941
6942         /**
6943          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6944          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6945          * @param {String} selector The simple selector to test
6946          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6947                 search as a number or element (defaults to 10 || document.body)
6948          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6949          */
6950         up : function(simpleSelector, maxDepth){
6951             return this.findParentNode(simpleSelector, maxDepth, true);
6952         },
6953
6954
6955
6956         /**
6957          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @return {Boolean} True if this element matches the selector, else false
6960          */
6961         is : function(simpleSelector){
6962             return Roo.DomQuery.is(this.dom, simpleSelector);
6963         },
6964
6965         /**
6966          * Perform animation on this element.
6967          * @param {Object} args The YUI animation control args
6968          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6969          * @param {Function} onComplete (optional) Function to call when animation completes
6970          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6971          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6972          * @return {Roo.Element} this
6973          */
6974         animate : function(args, duration, onComplete, easing, animType){
6975             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6976             return this;
6977         },
6978
6979         /*
6980          * @private Internal animation call
6981          */
6982         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6983             animType = animType || 'run';
6984             opt = opt || {};
6985             var anim = Roo.lib.Anim[animType](
6986                 this.dom, args,
6987                 (opt.duration || defaultDur) || .35,
6988                 (opt.easing || defaultEase) || 'easeOut',
6989                 function(){
6990                     Roo.callback(cb, this);
6991                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6992                 },
6993                 this
6994             );
6995             opt.anim = anim;
6996             return anim;
6997         },
6998
6999         // private legacy anim prep
7000         preanim : function(a, i){
7001             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7002         },
7003
7004         /**
7005          * Removes worthless text nodes
7006          * @param {Boolean} forceReclean (optional) By default the element
7007          * keeps track if it has been cleaned already so
7008          * you can call this over and over. However, if you update the element and
7009          * need to force a reclean, you can pass true.
7010          */
7011         clean : function(forceReclean){
7012             if(this.isCleaned && forceReclean !== true){
7013                 return this;
7014             }
7015             var ns = /\S/;
7016             var d = this.dom, n = d.firstChild, ni = -1;
7017             while(n){
7018                 var nx = n.nextSibling;
7019                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7020                     d.removeChild(n);
7021                 }else{
7022                     n.nodeIndex = ++ni;
7023                 }
7024                 n = nx;
7025             }
7026             this.isCleaned = true;
7027             return this;
7028         },
7029
7030         // private
7031         calcOffsetsTo : function(el){
7032             el = Roo.get(el);
7033             var d = el.dom;
7034             var restorePos = false;
7035             if(el.getStyle('position') == 'static'){
7036                 el.position('relative');
7037                 restorePos = true;
7038             }
7039             var x = 0, y =0;
7040             var op = this.dom;
7041             while(op && op != d && op.tagName != 'HTML'){
7042                 x+= op.offsetLeft;
7043                 y+= op.offsetTop;
7044                 op = op.offsetParent;
7045             }
7046             if(restorePos){
7047                 el.position('static');
7048             }
7049             return [x, y];
7050         },
7051
7052         /**
7053          * Scrolls this element into view within the passed container.
7054          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7055          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7056          * @return {Roo.Element} this
7057          */
7058         scrollIntoView : function(container, hscroll){
7059             var c = Roo.getDom(container) || document.body;
7060             var el = this.dom;
7061
7062             var o = this.calcOffsetsTo(c),
7063                 l = o[0],
7064                 t = o[1],
7065                 b = t+el.offsetHeight,
7066                 r = l+el.offsetWidth;
7067
7068             var ch = c.clientHeight;
7069             var ct = parseInt(c.scrollTop, 10);
7070             var cl = parseInt(c.scrollLeft, 10);
7071             var cb = ct + ch;
7072             var cr = cl + c.clientWidth;
7073
7074             if(t < ct){
7075                 c.scrollTop = t;
7076             }else if(b > cb){
7077                 c.scrollTop = b-ch;
7078             }
7079
7080             if(hscroll !== false){
7081                 if(l < cl){
7082                     c.scrollLeft = l;
7083                 }else if(r > cr){
7084                     c.scrollLeft = r-c.clientWidth;
7085                 }
7086             }
7087             return this;
7088         },
7089
7090         // private
7091         scrollChildIntoView : function(child, hscroll){
7092             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7093         },
7094
7095         /**
7096          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7097          * the new height may not be available immediately.
7098          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7099          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7100          * @param {Function} onComplete (optional) Function to call when animation completes
7101          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7102          * @return {Roo.Element} this
7103          */
7104         autoHeight : function(animate, duration, onComplete, easing){
7105             var oldHeight = this.getHeight();
7106             this.clip();
7107             this.setHeight(1); // force clipping
7108             setTimeout(function(){
7109                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7110                 if(!animate){
7111                     this.setHeight(height);
7112                     this.unclip();
7113                     if(typeof onComplete == "function"){
7114                         onComplete();
7115                     }
7116                 }else{
7117                     this.setHeight(oldHeight); // restore original height
7118                     this.setHeight(height, animate, duration, function(){
7119                         this.unclip();
7120                         if(typeof onComplete == "function") onComplete();
7121                     }.createDelegate(this), easing);
7122                 }
7123             }.createDelegate(this), 0);
7124             return this;
7125         },
7126
7127         /**
7128          * Returns true if this element is an ancestor of the passed element
7129          * @param {HTMLElement/String} el The element to check
7130          * @return {Boolean} True if this element is an ancestor of el, else false
7131          */
7132         contains : function(el){
7133             if(!el){return false;}
7134             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7135         },
7136
7137         /**
7138          * Checks whether the element is currently visible using both visibility and display properties.
7139          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7140          * @return {Boolean} True if the element is currently visible, else false
7141          */
7142         isVisible : function(deep) {
7143             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7144             if(deep !== true || !vis){
7145                 return vis;
7146             }
7147             var p = this.dom.parentNode;
7148             while(p && p.tagName.toLowerCase() != "body"){
7149                 if(!Roo.fly(p, '_isVisible').isVisible()){
7150                     return false;
7151                 }
7152                 p = p.parentNode;
7153             }
7154             return true;
7155         },
7156
7157         /**
7158          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7159          * @param {String} selector The CSS selector
7160          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7161          * @return {CompositeElement/CompositeElementLite} The composite element
7162          */
7163         select : function(selector, unique){
7164             return El.select(selector, unique, this.dom);
7165         },
7166
7167         /**
7168          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7169          * @param {String} selector The CSS selector
7170          * @return {Array} An array of the matched nodes
7171          */
7172         query : function(selector, unique){
7173             return Roo.DomQuery.select(selector, this.dom);
7174         },
7175
7176         /**
7177          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7178          * @param {String} selector The CSS selector
7179          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7180          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7181          */
7182         child : function(selector, returnDom){
7183             var n = Roo.DomQuery.selectNode(selector, this.dom);
7184             return returnDom ? n : Roo.get(n);
7185         },
7186
7187         /**
7188          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7189          * @param {String} selector The CSS selector
7190          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7191          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7192          */
7193         down : function(selector, returnDom){
7194             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7195             return returnDom ? n : Roo.get(n);
7196         },
7197
7198         /**
7199          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7200          * @param {String} group The group the DD object is member of
7201          * @param {Object} config The DD config object
7202          * @param {Object} overrides An object containing methods to override/implement on the DD object
7203          * @return {Roo.dd.DD} The DD object
7204          */
7205         initDD : function(group, config, overrides){
7206             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7207             return Roo.apply(dd, overrides);
7208         },
7209
7210         /**
7211          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7212          * @param {String} group The group the DDProxy object is member of
7213          * @param {Object} config The DDProxy config object
7214          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7215          * @return {Roo.dd.DDProxy} The DDProxy object
7216          */
7217         initDDProxy : function(group, config, overrides){
7218             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7219             return Roo.apply(dd, overrides);
7220         },
7221
7222         /**
7223          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7224          * @param {String} group The group the DDTarget object is member of
7225          * @param {Object} config The DDTarget config object
7226          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7227          * @return {Roo.dd.DDTarget} The DDTarget object
7228          */
7229         initDDTarget : function(group, config, overrides){
7230             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7231             return Roo.apply(dd, overrides);
7232         },
7233
7234         /**
7235          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7236          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7237          * @param {Boolean} visible Whether the element is visible
7238          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7239          * @return {Roo.Element} this
7240          */
7241          setVisible : function(visible, animate){
7242             if(!animate || !A){
7243                 if(this.visibilityMode == El.DISPLAY){
7244                     this.setDisplayed(visible);
7245                 }else{
7246                     this.fixDisplay();
7247                     this.dom.style.visibility = visible ? "visible" : "hidden";
7248                 }
7249             }else{
7250                 // closure for composites
7251                 var dom = this.dom;
7252                 var visMode = this.visibilityMode;
7253                 if(visible){
7254                     this.setOpacity(.01);
7255                     this.setVisible(true);
7256                 }
7257                 this.anim({opacity: { to: (visible?1:0) }},
7258                       this.preanim(arguments, 1),
7259                       null, .35, 'easeIn', function(){
7260                          if(!visible){
7261                              if(visMode == El.DISPLAY){
7262                                  dom.style.display = "none";
7263                              }else{
7264                                  dom.style.visibility = "hidden";
7265                              }
7266                              Roo.get(dom).setOpacity(1);
7267                          }
7268                      });
7269             }
7270             return this;
7271         },
7272
7273         /**
7274          * Returns true if display is not "none"
7275          * @return {Boolean}
7276          */
7277         isDisplayed : function() {
7278             return this.getStyle("display") != "none";
7279         },
7280
7281         /**
7282          * Toggles the element's visibility or display, depending on visibility mode.
7283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7284          * @return {Roo.Element} this
7285          */
7286         toggle : function(animate){
7287             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7288             return this;
7289         },
7290
7291         /**
7292          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7293          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7294          * @return {Roo.Element} this
7295          */
7296         setDisplayed : function(value) {
7297             if(typeof value == "boolean"){
7298                value = value ? this.originalDisplay : "none";
7299             }
7300             this.setStyle("display", value);
7301             return this;
7302         },
7303
7304         /**
7305          * Tries to focus the element. Any exceptions are caught and ignored.
7306          * @return {Roo.Element} this
7307          */
7308         focus : function() {
7309             try{
7310                 this.dom.focus();
7311             }catch(e){}
7312             return this;
7313         },
7314
7315         /**
7316          * Tries to blur the element. Any exceptions are caught and ignored.
7317          * @return {Roo.Element} this
7318          */
7319         blur : function() {
7320             try{
7321                 this.dom.blur();
7322             }catch(e){}
7323             return this;
7324         },
7325
7326         /**
7327          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7328          * @param {String/Array} className The CSS class to add, or an array of classes
7329          * @return {Roo.Element} this
7330          */
7331         addClass : function(className){
7332             if(className instanceof Array){
7333                 for(var i = 0, len = className.length; i < len; i++) {
7334                     this.addClass(className[i]);
7335                 }
7336             }else{
7337                 if(className && !this.hasClass(className)){
7338                     this.dom.className = this.dom.className + " " + className;
7339                 }
7340             }
7341             return this;
7342         },
7343
7344         /**
7345          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7346          * @param {String/Array} className The CSS class to add, or an array of classes
7347          * @return {Roo.Element} this
7348          */
7349         radioClass : function(className){
7350             var siblings = this.dom.parentNode.childNodes;
7351             for(var i = 0; i < siblings.length; i++) {
7352                 var s = siblings[i];
7353                 if(s.nodeType == 1){
7354                     Roo.get(s).removeClass(className);
7355                 }
7356             }
7357             this.addClass(className);
7358             return this;
7359         },
7360
7361         /**
7362          * Removes one or more CSS classes from the element.
7363          * @param {String/Array} className The CSS class to remove, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         removeClass : function(className){
7367             if(!className || !this.dom.className){
7368                 return this;
7369             }
7370             if(className instanceof Array){
7371                 for(var i = 0, len = className.length; i < len; i++) {
7372                     this.removeClass(className[i]);
7373                 }
7374             }else{
7375                 if(this.hasClass(className)){
7376                     var re = this.classReCache[className];
7377                     if (!re) {
7378                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7379                        this.classReCache[className] = re;
7380                     }
7381                     this.dom.className =
7382                         this.dom.className.replace(re, " ");
7383                 }
7384             }
7385             return this;
7386         },
7387
7388         // private
7389         classReCache: {},
7390
7391         /**
7392          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7393          * @param {String} className The CSS class to toggle
7394          * @return {Roo.Element} this
7395          */
7396         toggleClass : function(className){
7397             if(this.hasClass(className)){
7398                 this.removeClass(className);
7399             }else{
7400                 this.addClass(className);
7401             }
7402             return this;
7403         },
7404
7405         /**
7406          * Checks if the specified CSS class exists on this element's DOM node.
7407          * @param {String} className The CSS class to check for
7408          * @return {Boolean} True if the class exists, else false
7409          */
7410         hasClass : function(className){
7411             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7412         },
7413
7414         /**
7415          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7416          * @param {String} oldClassName The CSS class to replace
7417          * @param {String} newClassName The replacement CSS class
7418          * @return {Roo.Element} this
7419          */
7420         replaceClass : function(oldClassName, newClassName){
7421             this.removeClass(oldClassName);
7422             this.addClass(newClassName);
7423             return this;
7424         },
7425
7426         /**
7427          * Returns an object with properties matching the styles requested.
7428          * For example, el.getStyles('color', 'font-size', 'width') might return
7429          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7430          * @param {String} style1 A style name
7431          * @param {String} style2 A style name
7432          * @param {String} etc.
7433          * @return {Object} The style object
7434          */
7435         getStyles : function(){
7436             var a = arguments, len = a.length, r = {};
7437             for(var i = 0; i < len; i++){
7438                 r[a[i]] = this.getStyle(a[i]);
7439             }
7440             return r;
7441         },
7442
7443         /**
7444          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7445          * @param {String} property The style property whose value is returned.
7446          * @return {String} The current value of the style property for this element.
7447          */
7448         getStyle : function(){
7449             return view && view.getComputedStyle ?
7450                 function(prop){
7451                     var el = this.dom, v, cs, camel;
7452                     if(prop == 'float'){
7453                         prop = "cssFloat";
7454                     }
7455                     if(el.style && (v = el.style[prop])){
7456                         return v;
7457                     }
7458                     if(cs = view.getComputedStyle(el, "")){
7459                         if(!(camel = propCache[prop])){
7460                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7461                         }
7462                         return cs[camel];
7463                     }
7464                     return null;
7465                 } :
7466                 function(prop){
7467                     var el = this.dom, v, cs, camel;
7468                     if(prop == 'opacity'){
7469                         if(typeof el.style.filter == 'string'){
7470                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7471                             if(m){
7472                                 var fv = parseFloat(m[1]);
7473                                 if(!isNaN(fv)){
7474                                     return fv ? fv / 100 : 0;
7475                                 }
7476                             }
7477                         }
7478                         return 1;
7479                     }else if(prop == 'float'){
7480                         prop = "styleFloat";
7481                     }
7482                     if(!(camel = propCache[prop])){
7483                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7484                     }
7485                     if(v = el.style[camel]){
7486                         return v;
7487                     }
7488                     if(cs = el.currentStyle){
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 };
7493         }(),
7494
7495         /**
7496          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7497          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7498          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7499          * @return {Roo.Element} this
7500          */
7501         setStyle : function(prop, value){
7502             if(typeof prop == "string"){
7503                 
7504                 if (prop == 'float') {
7505                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7506                     return this;
7507                 }
7508                 
7509                 var camel;
7510                 if(!(camel = propCache[prop])){
7511                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7512                 }
7513                 
7514                 if(camel == 'opacity') {
7515                     this.setOpacity(value);
7516                 }else{
7517                     this.dom.style[camel] = value;
7518                 }
7519             }else{
7520                 for(var style in prop){
7521                     if(typeof prop[style] != "function"){
7522                        this.setStyle(style, prop[style]);
7523                     }
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         /**
7530          * More flexible version of {@link #setStyle} for setting style properties.
7531          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7532          * a function which returns such a specification.
7533          * @return {Roo.Element} this
7534          */
7535         applyStyles : function(style){
7536             Roo.DomHelper.applyStyles(this.dom, style);
7537             return this;
7538         },
7539
7540         /**
7541           * 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).
7542           * @return {Number} The X position of the element
7543           */
7544         getX : function(){
7545             return D.getX(this.dom);
7546         },
7547
7548         /**
7549           * 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).
7550           * @return {Number} The Y position of the element
7551           */
7552         getY : function(){
7553             return D.getY(this.dom);
7554         },
7555
7556         /**
7557           * 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).
7558           * @return {Array} The XY position of the element
7559           */
7560         getXY : function(){
7561             return D.getXY(this.dom);
7562         },
7563
7564         /**
7565          * 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).
7566          * @param {Number} The X position of the element
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         setX : function(x, animate){
7571             if(!animate || !A){
7572                 D.setX(this.dom, x);
7573             }else{
7574                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * 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).
7581          * @param {Number} The Y position of the element
7582          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7583          * @return {Roo.Element} this
7584          */
7585         setY : function(y, animate){
7586             if(!animate || !A){
7587                 D.setY(this.dom, y);
7588             }else{
7589                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7590             }
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7596          * @param {String} left The left CSS property value
7597          * @return {Roo.Element} this
7598          */
7599         setLeft : function(left){
7600             this.setStyle("left", this.addUnits(left));
7601             return this;
7602         },
7603
7604         /**
7605          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7606          * @param {String} top The top CSS property value
7607          * @return {Roo.Element} this
7608          */
7609         setTop : function(top){
7610             this.setStyle("top", this.addUnits(top));
7611             return this;
7612         },
7613
7614         /**
7615          * Sets the element's CSS right style.
7616          * @param {String} right The right CSS property value
7617          * @return {Roo.Element} this
7618          */
7619         setRight : function(right){
7620             this.setStyle("right", this.addUnits(right));
7621             return this;
7622         },
7623
7624         /**
7625          * Sets the element's CSS bottom style.
7626          * @param {String} bottom The bottom CSS property value
7627          * @return {Roo.Element} this
7628          */
7629         setBottom : function(bottom){
7630             this.setStyle("bottom", this.addUnits(bottom));
7631             return this;
7632         },
7633
7634         /**
7635          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7636          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7637          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7638          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7639          * @return {Roo.Element} this
7640          */
7641         setXY : function(pos, animate){
7642             if(!animate || !A){
7643                 D.setXY(this.dom, pos);
7644             }else{
7645                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7646             }
7647             return this;
7648         },
7649
7650         /**
7651          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7652          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7653          * @param {Number} x X value for new position (coordinates are page-based)
7654          * @param {Number} y Y value for new position (coordinates are page-based)
7655          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7656          * @return {Roo.Element} this
7657          */
7658         setLocation : function(x, y, animate){
7659             this.setXY([x, y], this.preanim(arguments, 2));
7660             return this;
7661         },
7662
7663         /**
7664          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7665          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7666          * @param {Number} x X value for new position (coordinates are page-based)
7667          * @param {Number} y Y value for new position (coordinates are page-based)
7668          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         moveTo : function(x, y, animate){
7672             this.setXY([x, y], this.preanim(arguments, 2));
7673             return this;
7674         },
7675
7676         /**
7677          * Returns the region of the given element.
7678          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7679          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7680          */
7681         getRegion : function(){
7682             return D.getRegion(this.dom);
7683         },
7684
7685         /**
7686          * Returns the offset height of the element
7687          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7688          * @return {Number} The element's height
7689          */
7690         getHeight : function(contentHeight){
7691             var h = this.dom.offsetHeight || 0;
7692             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7693         },
7694
7695         /**
7696          * Returns the offset width of the element
7697          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7698          * @return {Number} The element's width
7699          */
7700         getWidth : function(contentWidth){
7701             var w = this.dom.offsetWidth || 0;
7702             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7703         },
7704
7705         /**
7706          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7707          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7708          * if a height has not been set using CSS.
7709          * @return {Number}
7710          */
7711         getComputedHeight : function(){
7712             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7713             if(!h){
7714                 h = parseInt(this.getStyle('height'), 10) || 0;
7715                 if(!this.isBorderBox()){
7716                     h += this.getFrameWidth('tb');
7717                 }
7718             }
7719             return h;
7720         },
7721
7722         /**
7723          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7724          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7725          * if a width has not been set using CSS.
7726          * @return {Number}
7727          */
7728         getComputedWidth : function(){
7729             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7730             if(!w){
7731                 w = parseInt(this.getStyle('width'), 10) || 0;
7732                 if(!this.isBorderBox()){
7733                     w += this.getFrameWidth('lr');
7734                 }
7735             }
7736             return w;
7737         },
7738
7739         /**
7740          * Returns the size of the element.
7741          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7742          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7743          */
7744         getSize : function(contentSize){
7745             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7746         },
7747
7748         /**
7749          * Returns the width and height of the viewport.
7750          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7751          */
7752         getViewSize : function(){
7753             var d = this.dom, doc = document, aw = 0, ah = 0;
7754             if(d == doc || d == doc.body){
7755                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7756             }else{
7757                 return {
7758                     width : d.clientWidth,
7759                     height: d.clientHeight
7760                 };
7761             }
7762         },
7763
7764         /**
7765          * Returns the value of the "value" attribute
7766          * @param {Boolean} asNumber true to parse the value as a number
7767          * @return {String/Number}
7768          */
7769         getValue : function(asNumber){
7770             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7771         },
7772
7773         // private
7774         adjustWidth : function(width){
7775             if(typeof width == "number"){
7776                 if(this.autoBoxAdjust && !this.isBorderBox()){
7777                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7778                 }
7779                 if(width < 0){
7780                     width = 0;
7781                 }
7782             }
7783             return width;
7784         },
7785
7786         // private
7787         adjustHeight : function(height){
7788             if(typeof height == "number"){
7789                if(this.autoBoxAdjust && !this.isBorderBox()){
7790                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7791                }
7792                if(height < 0){
7793                    height = 0;
7794                }
7795             }
7796             return height;
7797         },
7798
7799         /**
7800          * Set the width of the element
7801          * @param {Number} width The new width
7802          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7803          * @return {Roo.Element} this
7804          */
7805         setWidth : function(width, animate){
7806             width = this.adjustWidth(width);
7807             if(!animate || !A){
7808                 this.dom.style.width = this.addUnits(width);
7809             }else{
7810                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7811             }
7812             return this;
7813         },
7814
7815         /**
7816          * Set the height of the element
7817          * @param {Number} height The new height
7818          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821          setHeight : function(height, animate){
7822             height = this.adjustHeight(height);
7823             if(!animate || !A){
7824                 this.dom.style.height = this.addUnits(height);
7825             }else{
7826                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7827             }
7828             return this;
7829         },
7830
7831         /**
7832          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7833          * @param {Number} width The new width
7834          * @param {Number} height The new height
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          setSize : function(width, height, animate){
7839             if(typeof width == "object"){ // in case of object from getSize()
7840                 height = width.height; width = width.width;
7841             }
7842             width = this.adjustWidth(width); height = this.adjustHeight(height);
7843             if(!animate || !A){
7844                 this.dom.style.width = this.addUnits(width);
7845                 this.dom.style.height = this.addUnits(height);
7846             }else{
7847                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7848             }
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7854          * @param {Number} x X value for new position (coordinates are page-based)
7855          * @param {Number} y Y value for new position (coordinates are page-based)
7856          * @param {Number} width The new width
7857          * @param {Number} height The new height
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setBounds : function(x, y, width, height, animate){
7862             if(!animate || !A){
7863                 this.setSize(width, height);
7864                 this.setLocation(x, y);
7865             }else{
7866                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7867                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7868                               this.preanim(arguments, 4), 'motion');
7869             }
7870             return this;
7871         },
7872
7873         /**
7874          * 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.
7875          * @param {Roo.lib.Region} region The region to fill
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setRegion : function(region, animate){
7880             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7881             return this;
7882         },
7883
7884         /**
7885          * Appends an event handler
7886          *
7887          * @param {String}   eventName     The type of event to append
7888          * @param {Function} fn        The method the event invokes
7889          * @param {Object} scope       (optional) The scope (this object) of the fn
7890          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7891          */
7892         addListener : function(eventName, fn, scope, options){
7893             if (this.dom) {
7894                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7895             }
7896         },
7897
7898         /**
7899          * Removes an event handler from this element
7900          * @param {String} eventName the type of event to remove
7901          * @param {Function} fn the method the event invokes
7902          * @return {Roo.Element} this
7903          */
7904         removeListener : function(eventName, fn){
7905             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7906             return this;
7907         },
7908
7909         /**
7910          * Removes all previous added listeners from this element
7911          * @return {Roo.Element} this
7912          */
7913         removeAllListeners : function(){
7914             E.purgeElement(this.dom);
7915             return this;
7916         },
7917
7918         relayEvent : function(eventName, observable){
7919             this.on(eventName, function(e){
7920                 observable.fireEvent(eventName, e);
7921             });
7922         },
7923
7924         /**
7925          * Set the opacity of the element
7926          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setOpacity : function(opacity, animate){
7931             if(!animate || !A){
7932                 var s = this.dom.style;
7933                 if(Roo.isIE){
7934                     s.zoom = 1;
7935                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7936                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7937                 }else{
7938                     s.opacity = opacity;
7939                 }
7940             }else{
7941                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Gets the left X coordinate
7948          * @param {Boolean} local True to get the local css position instead of page coordinate
7949          * @return {Number}
7950          */
7951         getLeft : function(local){
7952             if(!local){
7953                 return this.getX();
7954             }else{
7955                 return parseInt(this.getStyle("left"), 10) || 0;
7956             }
7957         },
7958
7959         /**
7960          * Gets the right X coordinate of the element (element X position + element width)
7961          * @param {Boolean} local True to get the local css position instead of page coordinate
7962          * @return {Number}
7963          */
7964         getRight : function(local){
7965             if(!local){
7966                 return this.getX() + this.getWidth();
7967             }else{
7968                 return (this.getLeft(true) + this.getWidth()) || 0;
7969             }
7970         },
7971
7972         /**
7973          * Gets the top Y coordinate
7974          * @param {Boolean} local True to get the local css position instead of page coordinate
7975          * @return {Number}
7976          */
7977         getTop : function(local) {
7978             if(!local){
7979                 return this.getY();
7980             }else{
7981                 return parseInt(this.getStyle("top"), 10) || 0;
7982             }
7983         },
7984
7985         /**
7986          * Gets the bottom Y coordinate of the element (element Y position + element height)
7987          * @param {Boolean} local True to get the local css position instead of page coordinate
7988          * @return {Number}
7989          */
7990         getBottom : function(local){
7991             if(!local){
7992                 return this.getY() + this.getHeight();
7993             }else{
7994                 return (this.getTop(true) + this.getHeight()) || 0;
7995             }
7996         },
7997
7998         /**
7999         * Initializes positioning on this element. If a desired position is not passed, it will make the
8000         * the element positioned relative IF it is not already positioned.
8001         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8002         * @param {Number} zIndex (optional) The zIndex to apply
8003         * @param {Number} x (optional) Set the page X position
8004         * @param {Number} y (optional) Set the page Y position
8005         */
8006         position : function(pos, zIndex, x, y){
8007             if(!pos){
8008                if(this.getStyle('position') == 'static'){
8009                    this.setStyle('position', 'relative');
8010                }
8011             }else{
8012                 this.setStyle("position", pos);
8013             }
8014             if(zIndex){
8015                 this.setStyle("z-index", zIndex);
8016             }
8017             if(x !== undefined && y !== undefined){
8018                 this.setXY([x, y]);
8019             }else if(x !== undefined){
8020                 this.setX(x);
8021             }else if(y !== undefined){
8022                 this.setY(y);
8023             }
8024         },
8025
8026         /**
8027         * Clear positioning back to the default when the document was loaded
8028         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8029         * @return {Roo.Element} this
8030          */
8031         clearPositioning : function(value){
8032             value = value ||'';
8033             this.setStyle({
8034                 "left": value,
8035                 "right": value,
8036                 "top": value,
8037                 "bottom": value,
8038                 "z-index": "",
8039                 "position" : "static"
8040             });
8041             return this;
8042         },
8043
8044         /**
8045         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8046         * snapshot before performing an update and then restoring the element.
8047         * @return {Object}
8048         */
8049         getPositioning : function(){
8050             var l = this.getStyle("left");
8051             var t = this.getStyle("top");
8052             return {
8053                 "position" : this.getStyle("position"),
8054                 "left" : l,
8055                 "right" : l ? "" : this.getStyle("right"),
8056                 "top" : t,
8057                 "bottom" : t ? "" : this.getStyle("bottom"),
8058                 "z-index" : this.getStyle("z-index")
8059             };
8060         },
8061
8062         /**
8063          * Gets the width of the border(s) for the specified side(s)
8064          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8065          * passing lr would get the border (l)eft width + the border (r)ight width.
8066          * @return {Number} The width of the sides passed added together
8067          */
8068         getBorderWidth : function(side){
8069             return this.addStyles(side, El.borders);
8070         },
8071
8072         /**
8073          * Gets the width of the padding(s) for the specified side(s)
8074          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8075          * passing lr would get the padding (l)eft + the padding (r)ight.
8076          * @return {Number} The padding of the sides passed added together
8077          */
8078         getPadding : function(side){
8079             return this.addStyles(side, El.paddings);
8080         },
8081
8082         /**
8083         * Set positioning with an object returned by getPositioning().
8084         * @param {Object} posCfg
8085         * @return {Roo.Element} this
8086          */
8087         setPositioning : function(pc){
8088             this.applyStyles(pc);
8089             if(pc.right == "auto"){
8090                 this.dom.style.right = "";
8091             }
8092             if(pc.bottom == "auto"){
8093                 this.dom.style.bottom = "";
8094             }
8095             return this;
8096         },
8097
8098         // private
8099         fixDisplay : function(){
8100             if(this.getStyle("display") == "none"){
8101                 this.setStyle("visibility", "hidden");
8102                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8103                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8104                     this.setStyle("display", "block");
8105                 }
8106             }
8107         },
8108
8109         /**
8110          * Quick set left and top adding default units
8111          * @param {String} left The left CSS property value
8112          * @param {String} top The top CSS property value
8113          * @return {Roo.Element} this
8114          */
8115          setLeftTop : function(left, top){
8116             this.dom.style.left = this.addUnits(left);
8117             this.dom.style.top = this.addUnits(top);
8118             return this;
8119         },
8120
8121         /**
8122          * Move this element relative to its current position.
8123          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8124          * @param {Number} distance How far to move the element in pixels
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          move : function(direction, distance, animate){
8129             var xy = this.getXY();
8130             direction = direction.toLowerCase();
8131             switch(direction){
8132                 case "l":
8133                 case "left":
8134                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8135                     break;
8136                case "r":
8137                case "right":
8138                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8139                     break;
8140                case "t":
8141                case "top":
8142                case "up":
8143                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8144                     break;
8145                case "b":
8146                case "bottom":
8147                case "down":
8148                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8149                     break;
8150             }
8151             return this;
8152         },
8153
8154         /**
8155          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8156          * @return {Roo.Element} this
8157          */
8158         clip : function(){
8159             if(!this.isClipped){
8160                this.isClipped = true;
8161                this.originalClip = {
8162                    "o": this.getStyle("overflow"),
8163                    "x": this.getStyle("overflow-x"),
8164                    "y": this.getStyle("overflow-y")
8165                };
8166                this.setStyle("overflow", "hidden");
8167                this.setStyle("overflow-x", "hidden");
8168                this.setStyle("overflow-y", "hidden");
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          *  Return clipping (overflow) to original clipping before clip() was called
8175          * @return {Roo.Element} this
8176          */
8177         unclip : function(){
8178             if(this.isClipped){
8179                 this.isClipped = false;
8180                 var o = this.originalClip;
8181                 if(o.o){this.setStyle("overflow", o.o);}
8182                 if(o.x){this.setStyle("overflow-x", o.x);}
8183                 if(o.y){this.setStyle("overflow-y", o.y);}
8184             }
8185             return this;
8186         },
8187
8188
8189         /**
8190          * Gets the x,y coordinates specified by the anchor position on the element.
8191          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8192          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8193          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8194          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8195          * @return {Array} [x, y] An array containing the element's x and y coordinates
8196          */
8197         getAnchorXY : function(anchor, local, s){
8198             //Passing a different size is useful for pre-calculating anchors,
8199             //especially for anchored animations that change the el size.
8200
8201             var w, h, vp = false;
8202             if(!s){
8203                 var d = this.dom;
8204                 if(d == document.body || d == document){
8205                     vp = true;
8206                     w = D.getViewWidth(); h = D.getViewHeight();
8207                 }else{
8208                     w = this.getWidth(); h = this.getHeight();
8209                 }
8210             }else{
8211                 w = s.width;  h = s.height;
8212             }
8213             var x = 0, y = 0, r = Math.round;
8214             switch((anchor || "tl").toLowerCase()){
8215                 case "c":
8216                     x = r(w*.5);
8217                     y = r(h*.5);
8218                 break;
8219                 case "t":
8220                     x = r(w*.5);
8221                     y = 0;
8222                 break;
8223                 case "l":
8224                     x = 0;
8225                     y = r(h*.5);
8226                 break;
8227                 case "r":
8228                     x = w;
8229                     y = r(h*.5);
8230                 break;
8231                 case "b":
8232                     x = r(w*.5);
8233                     y = h;
8234                 break;
8235                 case "tl":
8236                     x = 0;
8237                     y = 0;
8238                 break;
8239                 case "bl":
8240                     x = 0;
8241                     y = h;
8242                 break;
8243                 case "br":
8244                     x = w;
8245                     y = h;
8246                 break;
8247                 case "tr":
8248                     x = w;
8249                     y = 0;
8250                 break;
8251             }
8252             if(local === true){
8253                 return [x, y];
8254             }
8255             if(vp){
8256                 var sc = this.getScroll();
8257                 return [x + sc.left, y + sc.top];
8258             }
8259             //Add the element's offset xy
8260             var o = this.getXY();
8261             return [x+o[0], y+o[1]];
8262         },
8263
8264         /**
8265          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8266          * supported position values.
8267          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8268          * @param {String} position The position to align to.
8269          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8270          * @return {Array} [x, y]
8271          */
8272         getAlignToXY : function(el, p, o){
8273             el = Roo.get(el);
8274             var d = this.dom;
8275             if(!el.dom){
8276                 throw "Element.alignTo with an element that doesn't exist";
8277             }
8278             var c = false; //constrain to viewport
8279             var p1 = "", p2 = "";
8280             o = o || [0,0];
8281
8282             if(!p){
8283                 p = "tl-bl";
8284             }else if(p == "?"){
8285                 p = "tl-bl?";
8286             }else if(p.indexOf("-") == -1){
8287                 p = "tl-" + p;
8288             }
8289             p = p.toLowerCase();
8290             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8291             if(!m){
8292                throw "Element.alignTo with an invalid alignment " + p;
8293             }
8294             p1 = m[1]; p2 = m[2]; c = !!m[3];
8295
8296             //Subtract the aligned el's internal xy from the target's offset xy
8297             //plus custom offset to get the aligned el's new offset xy
8298             var a1 = this.getAnchorXY(p1, true);
8299             var a2 = el.getAnchorXY(p2, false);
8300             var x = a2[0] - a1[0] + o[0];
8301             var y = a2[1] - a1[1] + o[1];
8302             if(c){
8303                 //constrain the aligned el to viewport if necessary
8304                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8305                 // 5px of margin for ie
8306                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8307
8308                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8309                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8310                 //otherwise swap the aligned el to the opposite border of the target.
8311                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8312                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8313                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8314                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8315
8316                var doc = document;
8317                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8318                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8319
8320                if((x+w) > dw + scrollX){
8321                     x = swapX ? r.left-w : dw+scrollX-w;
8322                 }
8323                if(x < scrollX){
8324                    x = swapX ? r.right : scrollX;
8325                }
8326                if((y+h) > dh + scrollY){
8327                     y = swapY ? r.top-h : dh+scrollY-h;
8328                 }
8329                if (y < scrollY){
8330                    y = swapY ? r.bottom : scrollY;
8331                }
8332             }
8333             return [x,y];
8334         },
8335
8336         // private
8337         getConstrainToXY : function(){
8338             var os = {top:0, left:0, bottom:0, right: 0};
8339
8340             return function(el, local, offsets, proposedXY){
8341                 el = Roo.get(el);
8342                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8343
8344                 var vw, vh, vx = 0, vy = 0;
8345                 if(el.dom == document.body || el.dom == document){
8346                     vw = Roo.lib.Dom.getViewWidth();
8347                     vh = Roo.lib.Dom.getViewHeight();
8348                 }else{
8349                     vw = el.dom.clientWidth;
8350                     vh = el.dom.clientHeight;
8351                     if(!local){
8352                         var vxy = el.getXY();
8353                         vx = vxy[0];
8354                         vy = vxy[1];
8355                     }
8356                 }
8357
8358                 var s = el.getScroll();
8359
8360                 vx += offsets.left + s.left;
8361                 vy += offsets.top + s.top;
8362
8363                 vw -= offsets.right;
8364                 vh -= offsets.bottom;
8365
8366                 var vr = vx+vw;
8367                 var vb = vy+vh;
8368
8369                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8370                 var x = xy[0], y = xy[1];
8371                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8372
8373                 // only move it if it needs it
8374                 var moved = false;
8375
8376                 // first validate right/bottom
8377                 if((x + w) > vr){
8378                     x = vr - w;
8379                     moved = true;
8380                 }
8381                 if((y + h) > vb){
8382                     y = vb - h;
8383                     moved = true;
8384                 }
8385                 // then make sure top/left isn't negative
8386                 if(x < vx){
8387                     x = vx;
8388                     moved = true;
8389                 }
8390                 if(y < vy){
8391                     y = vy;
8392                     moved = true;
8393                 }
8394                 return moved ? [x, y] : false;
8395             };
8396         }(),
8397
8398         // private
8399         adjustForConstraints : function(xy, parent, offsets){
8400             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8401         },
8402
8403         /**
8404          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8405          * document it aligns it to the viewport.
8406          * The position parameter is optional, and can be specified in any one of the following formats:
8407          * <ul>
8408          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8409          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8410          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8411          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8412          *   <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
8413          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8414          * </ul>
8415          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8416          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8417          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8418          * that specified in order to enforce the viewport constraints.
8419          * Following are all of the supported anchor positions:
8420     <pre>
8421     Value  Description
8422     -----  -----------------------------
8423     tl     The top left corner (default)
8424     t      The center of the top edge
8425     tr     The top right corner
8426     l      The center of the left edge
8427     c      In the center of the element
8428     r      The center of the right edge
8429     bl     The bottom left corner
8430     b      The center of the bottom edge
8431     br     The bottom right corner
8432     </pre>
8433     Example Usage:
8434     <pre><code>
8435     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8436     el.alignTo("other-el");
8437
8438     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8439     el.alignTo("other-el", "tr?");
8440
8441     // align the bottom right corner of el with the center left edge of other-el
8442     el.alignTo("other-el", "br-l?");
8443
8444     // align the center of el with the bottom left corner of other-el and
8445     // adjust the x position by -6 pixels (and the y position by 0)
8446     el.alignTo("other-el", "c-bl", [-6, 0]);
8447     </code></pre>
8448          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8449          * @param {String} position The position to align to.
8450          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8451          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8452          * @return {Roo.Element} this
8453          */
8454         alignTo : function(element, position, offsets, animate){
8455             var xy = this.getAlignToXY(element, position, offsets);
8456             this.setXY(xy, this.preanim(arguments, 3));
8457             return this;
8458         },
8459
8460         /**
8461          * Anchors an element to another element and realigns it when the window is resized.
8462          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8463          * @param {String} position The position to align to.
8464          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8465          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8466          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8467          * is a number, it is used as the buffer delay (defaults to 50ms).
8468          * @param {Function} callback The function to call after the animation finishes
8469          * @return {Roo.Element} this
8470          */
8471         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8472             var action = function(){
8473                 this.alignTo(el, alignment, offsets, animate);
8474                 Roo.callback(callback, this);
8475             };
8476             Roo.EventManager.onWindowResize(action, this);
8477             var tm = typeof monitorScroll;
8478             if(tm != 'undefined'){
8479                 Roo.EventManager.on(window, 'scroll', action, this,
8480                     {buffer: tm == 'number' ? monitorScroll : 50});
8481             }
8482             action.call(this); // align immediately
8483             return this;
8484         },
8485         /**
8486          * Clears any opacity settings from this element. Required in some cases for IE.
8487          * @return {Roo.Element} this
8488          */
8489         clearOpacity : function(){
8490             if (window.ActiveXObject) {
8491                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8492                     this.dom.style.filter = "";
8493                 }
8494             } else {
8495                 this.dom.style.opacity = "";
8496                 this.dom.style["-moz-opacity"] = "";
8497                 this.dom.style["-khtml-opacity"] = "";
8498             }
8499             return this;
8500         },
8501
8502         /**
8503          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8504          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8505          * @return {Roo.Element} this
8506          */
8507         hide : function(animate){
8508             this.setVisible(false, this.preanim(arguments, 0));
8509             return this;
8510         },
8511
8512         /**
8513         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8514         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8515          * @return {Roo.Element} this
8516          */
8517         show : function(animate){
8518             this.setVisible(true, this.preanim(arguments, 0));
8519             return this;
8520         },
8521
8522         /**
8523          * @private Test if size has a unit, otherwise appends the default
8524          */
8525         addUnits : function(size){
8526             return Roo.Element.addUnits(size, this.defaultUnit);
8527         },
8528
8529         /**
8530          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8531          * @return {Roo.Element} this
8532          */
8533         beginMeasure : function(){
8534             var el = this.dom;
8535             if(el.offsetWidth || el.offsetHeight){
8536                 return this; // offsets work already
8537             }
8538             var changed = [];
8539             var p = this.dom, b = document.body; // start with this element
8540             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8541                 var pe = Roo.get(p);
8542                 if(pe.getStyle('display') == 'none'){
8543                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8544                     p.style.visibility = "hidden";
8545                     p.style.display = "block";
8546                 }
8547                 p = p.parentNode;
8548             }
8549             this._measureChanged = changed;
8550             return this;
8551
8552         },
8553
8554         /**
8555          * Restores displays to before beginMeasure was called
8556          * @return {Roo.Element} this
8557          */
8558         endMeasure : function(){
8559             var changed = this._measureChanged;
8560             if(changed){
8561                 for(var i = 0, len = changed.length; i < len; i++) {
8562                     var r = changed[i];
8563                     r.el.style.visibility = r.visibility;
8564                     r.el.style.display = "none";
8565                 }
8566                 this._measureChanged = null;
8567             }
8568             return this;
8569         },
8570
8571         /**
8572         * Update the innerHTML of this element, optionally searching for and processing scripts
8573         * @param {String} html The new HTML
8574         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8575         * @param {Function} callback For async script loading you can be noticed when the update completes
8576         * @return {Roo.Element} this
8577          */
8578         update : function(html, loadScripts, callback){
8579             if(typeof html == "undefined"){
8580                 html = "";
8581             }
8582             if(loadScripts !== true){
8583                 this.dom.innerHTML = html;
8584                 if(typeof callback == "function"){
8585                     callback();
8586                 }
8587                 return this;
8588             }
8589             var id = Roo.id();
8590             var dom = this.dom;
8591
8592             html += '<span id="' + id + '"></span>';
8593
8594             E.onAvailable(id, function(){
8595                 var hd = document.getElementsByTagName("head")[0];
8596                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8597                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8598                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8599
8600                 var match;
8601                 while(match = re.exec(html)){
8602                     var attrs = match[1];
8603                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8604                     if(srcMatch && srcMatch[2]){
8605                        var s = document.createElement("script");
8606                        s.src = srcMatch[2];
8607                        var typeMatch = attrs.match(typeRe);
8608                        if(typeMatch && typeMatch[2]){
8609                            s.type = typeMatch[2];
8610                        }
8611                        hd.appendChild(s);
8612                     }else if(match[2] && match[2].length > 0){
8613                         if(window.execScript) {
8614                            window.execScript(match[2]);
8615                         } else {
8616                             /**
8617                              * eval:var:id
8618                              * eval:var:dom
8619                              * eval:var:html
8620                              * 
8621                              */
8622                            window.eval(match[2]);
8623                         }
8624                     }
8625                 }
8626                 var el = document.getElementById(id);
8627                 if(el){el.parentNode.removeChild(el);}
8628                 if(typeof callback == "function"){
8629                     callback();
8630                 }
8631             });
8632             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8633             return this;
8634         },
8635
8636         /**
8637          * Direct access to the UpdateManager update() method (takes the same parameters).
8638          * @param {String/Function} url The url for this request or a function to call to get the url
8639          * @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}
8640          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8641          * @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.
8642          * @return {Roo.Element} this
8643          */
8644         load : function(){
8645             var um = this.getUpdateManager();
8646             um.update.apply(um, arguments);
8647             return this;
8648         },
8649
8650         /**
8651         * Gets this element's UpdateManager
8652         * @return {Roo.UpdateManager} The UpdateManager
8653         */
8654         getUpdateManager : function(){
8655             if(!this.updateManager){
8656                 this.updateManager = new Roo.UpdateManager(this);
8657             }
8658             return this.updateManager;
8659         },
8660
8661         /**
8662          * Disables text selection for this element (normalized across browsers)
8663          * @return {Roo.Element} this
8664          */
8665         unselectable : function(){
8666             this.dom.unselectable = "on";
8667             this.swallowEvent("selectstart", true);
8668             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8669             this.addClass("x-unselectable");
8670             return this;
8671         },
8672
8673         /**
8674         * Calculates the x, y to center this element on the screen
8675         * @return {Array} The x, y values [x, y]
8676         */
8677         getCenterXY : function(){
8678             return this.getAlignToXY(document, 'c-c');
8679         },
8680
8681         /**
8682         * Centers the Element in either the viewport, or another Element.
8683         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8684         */
8685         center : function(centerIn){
8686             this.alignTo(centerIn || document, 'c-c');
8687             return this;
8688         },
8689
8690         /**
8691          * Tests various css rules/browsers to determine if this element uses a border box
8692          * @return {Boolean}
8693          */
8694         isBorderBox : function(){
8695             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8696         },
8697
8698         /**
8699          * Return a box {x, y, width, height} that can be used to set another elements
8700          * size/location to match this element.
8701          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8702          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8703          * @return {Object} box An object in the format {x, y, width, height}
8704          */
8705         getBox : function(contentBox, local){
8706             var xy;
8707             if(!local){
8708                 xy = this.getXY();
8709             }else{
8710                 var left = parseInt(this.getStyle("left"), 10) || 0;
8711                 var top = parseInt(this.getStyle("top"), 10) || 0;
8712                 xy = [left, top];
8713             }
8714             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8715             if(!contentBox){
8716                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8717             }else{
8718                 var l = this.getBorderWidth("l")+this.getPadding("l");
8719                 var r = this.getBorderWidth("r")+this.getPadding("r");
8720                 var t = this.getBorderWidth("t")+this.getPadding("t");
8721                 var b = this.getBorderWidth("b")+this.getPadding("b");
8722                 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)};
8723             }
8724             bx.right = bx.x + bx.width;
8725             bx.bottom = bx.y + bx.height;
8726             return bx;
8727         },
8728
8729         /**
8730          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8731          for more information about the sides.
8732          * @param {String} sides
8733          * @return {Number}
8734          */
8735         getFrameWidth : function(sides, onlyContentBox){
8736             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8737         },
8738
8739         /**
8740          * 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.
8741          * @param {Object} box The box to fill {x, y, width, height}
8742          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8743          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8744          * @return {Roo.Element} this
8745          */
8746         setBox : function(box, adjust, animate){
8747             var w = box.width, h = box.height;
8748             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8749                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8750                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8751             }
8752             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8753             return this;
8754         },
8755
8756         /**
8757          * Forces the browser to repaint this element
8758          * @return {Roo.Element} this
8759          */
8760          repaint : function(){
8761             var dom = this.dom;
8762             this.addClass("x-repaint");
8763             setTimeout(function(){
8764                 Roo.get(dom).removeClass("x-repaint");
8765             }, 1);
8766             return this;
8767         },
8768
8769         /**
8770          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8771          * then it returns the calculated width of the sides (see getPadding)
8772          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8773          * @return {Object/Number}
8774          */
8775         getMargins : function(side){
8776             if(!side){
8777                 return {
8778                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8779                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8780                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8781                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8782                 };
8783             }else{
8784                 return this.addStyles(side, El.margins);
8785              }
8786         },
8787
8788         // private
8789         addStyles : function(sides, styles){
8790             var val = 0, v, w;
8791             for(var i = 0, len = sides.length; i < len; i++){
8792                 v = this.getStyle(styles[sides.charAt(i)]);
8793                 if(v){
8794                      w = parseInt(v, 10);
8795                      if(w){ val += w; }
8796                 }
8797             }
8798             return val;
8799         },
8800
8801         /**
8802          * Creates a proxy element of this element
8803          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8804          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8805          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8806          * @return {Roo.Element} The new proxy element
8807          */
8808         createProxy : function(config, renderTo, matchBox){
8809             if(renderTo){
8810                 renderTo = Roo.getDom(renderTo);
8811             }else{
8812                 renderTo = document.body;
8813             }
8814             config = typeof config == "object" ?
8815                 config : {tag : "div", cls: config};
8816             var proxy = Roo.DomHelper.append(renderTo, config, true);
8817             if(matchBox){
8818                proxy.setBox(this.getBox());
8819             }
8820             return proxy;
8821         },
8822
8823         /**
8824          * Puts a mask over this element to disable user interaction. Requires core.css.
8825          * This method can only be applied to elements which accept child nodes.
8826          * @param {String} msg (optional) A message to display in the mask
8827          * @param {String} msgCls (optional) A css class to apply to the msg element
8828          * @return {Element} The mask  element
8829          */
8830         mask : function(msg, msgCls){
8831             if(this.getStyle("position") == "static"){
8832                 this.setStyle("position", "relative");
8833             }
8834             if(!this._mask){
8835                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8836             }
8837             this.addClass("x-masked");
8838             this._mask.setDisplayed(true);
8839             if(typeof msg == 'string'){
8840                 if(!this._maskMsg){
8841                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8842                 }
8843                 var mm = this._maskMsg;
8844                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8845                 mm.dom.firstChild.innerHTML = msg;
8846                 mm.setDisplayed(true);
8847                 mm.center(this);
8848             }
8849             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8850                 this._mask.setHeight(this.getHeight());
8851             }
8852             return this._mask;
8853         },
8854
8855         /**
8856          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8857          * it is cached for reuse.
8858          */
8859         unmask : function(removeEl){
8860             if(this._mask){
8861                 if(removeEl === true){
8862                     this._mask.remove();
8863                     delete this._mask;
8864                     if(this._maskMsg){
8865                         this._maskMsg.remove();
8866                         delete this._maskMsg;
8867                     }
8868                 }else{
8869                     this._mask.setDisplayed(false);
8870                     if(this._maskMsg){
8871                         this._maskMsg.setDisplayed(false);
8872                     }
8873                 }
8874             }
8875             this.removeClass("x-masked");
8876         },
8877
8878         /**
8879          * Returns true if this element is masked
8880          * @return {Boolean}
8881          */
8882         isMasked : function(){
8883             return this._mask && this._mask.isVisible();
8884         },
8885
8886         /**
8887          * Creates an iframe shim for this element to keep selects and other windowed objects from
8888          * showing through.
8889          * @return {Roo.Element} The new shim element
8890          */
8891         createShim : function(){
8892             var el = document.createElement('iframe');
8893             el.frameBorder = 'no';
8894             el.className = 'roo-shim';
8895             if(Roo.isIE && Roo.isSecure){
8896                 el.src = Roo.SSL_SECURE_URL;
8897             }
8898             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8899             shim.autoBoxAdjust = false;
8900             return shim;
8901         },
8902
8903         /**
8904          * Removes this element from the DOM and deletes it from the cache
8905          */
8906         remove : function(){
8907             if(this.dom.parentNode){
8908                 this.dom.parentNode.removeChild(this.dom);
8909             }
8910             delete El.cache[this.dom.id];
8911         },
8912
8913         /**
8914          * Sets up event handlers to add and remove a css class when the mouse is over this element
8915          * @param {String} className
8916          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8917          * mouseout events for children elements
8918          * @return {Roo.Element} this
8919          */
8920         addClassOnOver : function(className, preventFlicker){
8921             this.on("mouseover", function(){
8922                 Roo.fly(this, '_internal').addClass(className);
8923             }, this.dom);
8924             var removeFn = function(e){
8925                 if(preventFlicker !== true || !e.within(this, true)){
8926                     Roo.fly(this, '_internal').removeClass(className);
8927                 }
8928             };
8929             this.on("mouseout", removeFn, this.dom);
8930             return this;
8931         },
8932
8933         /**
8934          * Sets up event handlers to add and remove a css class when this element has the focus
8935          * @param {String} className
8936          * @return {Roo.Element} this
8937          */
8938         addClassOnFocus : function(className){
8939             this.on("focus", function(){
8940                 Roo.fly(this, '_internal').addClass(className);
8941             }, this.dom);
8942             this.on("blur", function(){
8943                 Roo.fly(this, '_internal').removeClass(className);
8944             }, this.dom);
8945             return this;
8946         },
8947         /**
8948          * 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)
8949          * @param {String} className
8950          * @return {Roo.Element} this
8951          */
8952         addClassOnClick : function(className){
8953             var dom = this.dom;
8954             this.on("mousedown", function(){
8955                 Roo.fly(dom, '_internal').addClass(className);
8956                 var d = Roo.get(document);
8957                 var fn = function(){
8958                     Roo.fly(dom, '_internal').removeClass(className);
8959                     d.removeListener("mouseup", fn);
8960                 };
8961                 d.on("mouseup", fn);
8962             });
8963             return this;
8964         },
8965
8966         /**
8967          * Stops the specified event from bubbling and optionally prevents the default action
8968          * @param {String} eventName
8969          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8970          * @return {Roo.Element} this
8971          */
8972         swallowEvent : function(eventName, preventDefault){
8973             var fn = function(e){
8974                 e.stopPropagation();
8975                 if(preventDefault){
8976                     e.preventDefault();
8977                 }
8978             };
8979             if(eventName instanceof Array){
8980                 for(var i = 0, len = eventName.length; i < len; i++){
8981                      this.on(eventName[i], fn);
8982                 }
8983                 return this;
8984             }
8985             this.on(eventName, fn);
8986             return this;
8987         },
8988
8989         /**
8990          * @private
8991          */
8992       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8993
8994         /**
8995          * Sizes this element to its parent element's dimensions performing
8996          * neccessary box adjustments.
8997          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8998          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8999          * @return {Roo.Element} this
9000          */
9001         fitToParent : function(monitorResize, targetParent) {
9002           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9003           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9004           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9005             return;
9006           }
9007           var p = Roo.get(targetParent || this.dom.parentNode);
9008           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9009           if (monitorResize === true) {
9010             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9011             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9012           }
9013           return this;
9014         },
9015
9016         /**
9017          * Gets the next sibling, skipping text nodes
9018          * @return {HTMLElement} The next sibling or null
9019          */
9020         getNextSibling : function(){
9021             var n = this.dom.nextSibling;
9022             while(n && n.nodeType != 1){
9023                 n = n.nextSibling;
9024             }
9025             return n;
9026         },
9027
9028         /**
9029          * Gets the previous sibling, skipping text nodes
9030          * @return {HTMLElement} The previous sibling or null
9031          */
9032         getPrevSibling : function(){
9033             var n = this.dom.previousSibling;
9034             while(n && n.nodeType != 1){
9035                 n = n.previousSibling;
9036             }
9037             return n;
9038         },
9039
9040
9041         /**
9042          * Appends the passed element(s) to this element
9043          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9044          * @return {Roo.Element} this
9045          */
9046         appendChild: function(el){
9047             el = Roo.get(el);
9048             el.appendTo(this);
9049             return this;
9050         },
9051
9052         /**
9053          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9054          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9055          * automatically generated with the specified attributes.
9056          * @param {HTMLElement} insertBefore (optional) a child element of this element
9057          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9058          * @return {Roo.Element} The new child element
9059          */
9060         createChild: function(config, insertBefore, returnDom){
9061             config = config || {tag:'div'};
9062             if(insertBefore){
9063                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9064             }
9065             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9066         },
9067
9068         /**
9069          * Appends this element to the passed element
9070          * @param {String/HTMLElement/Element} el The new parent element
9071          * @return {Roo.Element} this
9072          */
9073         appendTo: function(el){
9074             el = Roo.getDom(el);
9075             el.appendChild(this.dom);
9076             return this;
9077         },
9078
9079         /**
9080          * Inserts this element before the passed element in the DOM
9081          * @param {String/HTMLElement/Element} el The element to insert before
9082          * @return {Roo.Element} this
9083          */
9084         insertBefore: function(el){
9085             el = Roo.getDom(el);
9086             el.parentNode.insertBefore(this.dom, el);
9087             return this;
9088         },
9089
9090         /**
9091          * Inserts this element after the passed element in the DOM
9092          * @param {String/HTMLElement/Element} el The element to insert after
9093          * @return {Roo.Element} this
9094          */
9095         insertAfter: function(el){
9096             el = Roo.getDom(el);
9097             el.parentNode.insertBefore(this.dom, el.nextSibling);
9098             return this;
9099         },
9100
9101         /**
9102          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9103          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9104          * @return {Roo.Element} The new child
9105          */
9106         insertFirst: function(el, returnDom){
9107             el = el || {};
9108             if(typeof el == 'object' && !el.nodeType){ // dh config
9109                 return this.createChild(el, this.dom.firstChild, returnDom);
9110             }else{
9111                 el = Roo.getDom(el);
9112                 this.dom.insertBefore(el, this.dom.firstChild);
9113                 return !returnDom ? Roo.get(el) : el;
9114             }
9115         },
9116
9117         /**
9118          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9119          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9120          * @param {String} where (optional) 'before' or 'after' defaults to before
9121          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9122          * @return {Roo.Element} the inserted Element
9123          */
9124         insertSibling: function(el, where, returnDom){
9125             where = where ? where.toLowerCase() : 'before';
9126             el = el || {};
9127             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9128
9129             if(typeof el == 'object' && !el.nodeType){ // dh config
9130                 if(where == 'after' && !this.dom.nextSibling){
9131                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9132                 }else{
9133                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9134                 }
9135
9136             }else{
9137                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9138                             where == 'before' ? this.dom : this.dom.nextSibling);
9139                 if(!returnDom){
9140                     rt = Roo.get(rt);
9141                 }
9142             }
9143             return rt;
9144         },
9145
9146         /**
9147          * Creates and wraps this element with another element
9148          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9149          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9150          * @return {HTMLElement/Element} The newly created wrapper element
9151          */
9152         wrap: function(config, returnDom){
9153             if(!config){
9154                 config = {tag: "div"};
9155             }
9156             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9157             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9158             return newEl;
9159         },
9160
9161         /**
9162          * Replaces the passed element with this element
9163          * @param {String/HTMLElement/Element} el The element to replace
9164          * @return {Roo.Element} this
9165          */
9166         replace: function(el){
9167             el = Roo.get(el);
9168             this.insertBefore(el);
9169             el.remove();
9170             return this;
9171         },
9172
9173         /**
9174          * Inserts an html fragment into this element
9175          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9176          * @param {String} html The HTML fragment
9177          * @param {Boolean} returnEl True to return an Roo.Element
9178          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9179          */
9180         insertHtml : function(where, html, returnEl){
9181             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9182             return returnEl ? Roo.get(el) : el;
9183         },
9184
9185         /**
9186          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9187          * @param {Object} o The object with the attributes
9188          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9189          * @return {Roo.Element} this
9190          */
9191         set : function(o, useSet){
9192             var el = this.dom;
9193             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9194             for(var attr in o){
9195                 if(attr == "style" || typeof o[attr] == "function") continue;
9196                 if(attr=="cls"){
9197                     el.className = o["cls"];
9198                 }else{
9199                     if(useSet) el.setAttribute(attr, o[attr]);
9200                     else el[attr] = o[attr];
9201                 }
9202             }
9203             if(o.style){
9204                 Roo.DomHelper.applyStyles(el, o.style);
9205             }
9206             return this;
9207         },
9208
9209         /**
9210          * Convenience method for constructing a KeyMap
9211          * @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:
9212          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9213          * @param {Function} fn The function to call
9214          * @param {Object} scope (optional) The scope of the function
9215          * @return {Roo.KeyMap} The KeyMap created
9216          */
9217         addKeyListener : function(key, fn, scope){
9218             var config;
9219             if(typeof key != "object" || key instanceof Array){
9220                 config = {
9221                     key: key,
9222                     fn: fn,
9223                     scope: scope
9224                 };
9225             }else{
9226                 config = {
9227                     key : key.key,
9228                     shift : key.shift,
9229                     ctrl : key.ctrl,
9230                     alt : key.alt,
9231                     fn: fn,
9232                     scope: scope
9233                 };
9234             }
9235             return new Roo.KeyMap(this, config);
9236         },
9237
9238         /**
9239          * Creates a KeyMap for this element
9240          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9241          * @return {Roo.KeyMap} The KeyMap created
9242          */
9243         addKeyMap : function(config){
9244             return new Roo.KeyMap(this, config);
9245         },
9246
9247         /**
9248          * Returns true if this element is scrollable.
9249          * @return {Boolean}
9250          */
9251          isScrollable : function(){
9252             var dom = this.dom;
9253             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9254         },
9255
9256         /**
9257          * 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().
9258          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9259          * @param {Number} value The new scroll value
9260          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9261          * @return {Element} this
9262          */
9263
9264         scrollTo : function(side, value, animate){
9265             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9266             if(!animate || !A){
9267                 this.dom[prop] = value;
9268             }else{
9269                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9270                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9271             }
9272             return this;
9273         },
9274
9275         /**
9276          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9277          * within this element's scrollable range.
9278          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9279          * @param {Number} distance How far to scroll the element in pixels
9280          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9281          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9282          * was scrolled as far as it could go.
9283          */
9284          scroll : function(direction, distance, animate){
9285              if(!this.isScrollable()){
9286                  return;
9287              }
9288              var el = this.dom;
9289              var l = el.scrollLeft, t = el.scrollTop;
9290              var w = el.scrollWidth, h = el.scrollHeight;
9291              var cw = el.clientWidth, ch = el.clientHeight;
9292              direction = direction.toLowerCase();
9293              var scrolled = false;
9294              var a = this.preanim(arguments, 2);
9295              switch(direction){
9296                  case "l":
9297                  case "left":
9298                      if(w - l > cw){
9299                          var v = Math.min(l + distance, w-cw);
9300                          this.scrollTo("left", v, a);
9301                          scrolled = true;
9302                      }
9303                      break;
9304                 case "r":
9305                 case "right":
9306                      if(l > 0){
9307                          var v = Math.max(l - distance, 0);
9308                          this.scrollTo("left", v, a);
9309                          scrolled = true;
9310                      }
9311                      break;
9312                 case "t":
9313                 case "top":
9314                 case "up":
9315                      if(t > 0){
9316                          var v = Math.max(t - distance, 0);
9317                          this.scrollTo("top", v, a);
9318                          scrolled = true;
9319                      }
9320                      break;
9321                 case "b":
9322                 case "bottom":
9323                 case "down":
9324                      if(h - t > ch){
9325                          var v = Math.min(t + distance, h-ch);
9326                          this.scrollTo("top", v, a);
9327                          scrolled = true;
9328                      }
9329                      break;
9330              }
9331              return scrolled;
9332         },
9333
9334         /**
9335          * Translates the passed page coordinates into left/top css values for this element
9336          * @param {Number/Array} x The page x or an array containing [x, y]
9337          * @param {Number} y The page y
9338          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9339          */
9340         translatePoints : function(x, y){
9341             if(typeof x == 'object' || x instanceof Array){
9342                 y = x[1]; x = x[0];
9343             }
9344             var p = this.getStyle('position');
9345             var o = this.getXY();
9346
9347             var l = parseInt(this.getStyle('left'), 10);
9348             var t = parseInt(this.getStyle('top'), 10);
9349
9350             if(isNaN(l)){
9351                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9352             }
9353             if(isNaN(t)){
9354                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9355             }
9356
9357             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9358         },
9359
9360         /**
9361          * Returns the current scroll position of the element.
9362          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9363          */
9364         getScroll : function(){
9365             var d = this.dom, doc = document;
9366             if(d == doc || d == doc.body){
9367                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9368                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9369                 return {left: l, top: t};
9370             }else{
9371                 return {left: d.scrollLeft, top: d.scrollTop};
9372             }
9373         },
9374
9375         /**
9376          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9377          * are convert to standard 6 digit hex color.
9378          * @param {String} attr The css attribute
9379          * @param {String} defaultValue The default value to use when a valid color isn't found
9380          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9381          * YUI color anims.
9382          */
9383         getColor : function(attr, defaultValue, prefix){
9384             var v = this.getStyle(attr);
9385             if(!v || v == "transparent" || v == "inherit") {
9386                 return defaultValue;
9387             }
9388             var color = typeof prefix == "undefined" ? "#" : prefix;
9389             if(v.substr(0, 4) == "rgb("){
9390                 var rvs = v.slice(4, v.length -1).split(",");
9391                 for(var i = 0; i < 3; i++){
9392                     var h = parseInt(rvs[i]).toString(16);
9393                     if(h < 16){
9394                         h = "0" + h;
9395                     }
9396                     color += h;
9397                 }
9398             } else {
9399                 if(v.substr(0, 1) == "#"){
9400                     if(v.length == 4) {
9401                         for(var i = 1; i < 4; i++){
9402                             var c = v.charAt(i);
9403                             color +=  c + c;
9404                         }
9405                     }else if(v.length == 7){
9406                         color += v.substr(1);
9407                     }
9408                 }
9409             }
9410             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9411         },
9412
9413         /**
9414          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9415          * gradient background, rounded corners and a 4-way shadow.
9416          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9417          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9418          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9419          * @return {Roo.Element} this
9420          */
9421         boxWrap : function(cls){
9422             cls = cls || 'x-box';
9423             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9424             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9425             return el;
9426         },
9427
9428         /**
9429          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9430          * @param {String} namespace The namespace in which to look for the attribute
9431          * @param {String} name The attribute name
9432          * @return {String} The attribute value
9433          */
9434         getAttributeNS : Roo.isIE ? function(ns, name){
9435             var d = this.dom;
9436             var type = typeof d[ns+":"+name];
9437             if(type != 'undefined' && type != 'unknown'){
9438                 return d[ns+":"+name];
9439             }
9440             return d[name];
9441         } : function(ns, name){
9442             var d = this.dom;
9443             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9444         }
9445     };
9446
9447     var ep = El.prototype;
9448
9449     /**
9450      * Appends an event handler (Shorthand for addListener)
9451      * @param {String}   eventName     The type of event to append
9452      * @param {Function} fn        The method the event invokes
9453      * @param {Object} scope       (optional) The scope (this object) of the fn
9454      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9455      * @method
9456      */
9457     ep.on = ep.addListener;
9458         // backwards compat
9459     ep.mon = ep.addListener;
9460
9461     /**
9462      * Removes an event handler from this element (shorthand for removeListener)
9463      * @param {String} eventName the type of event to remove
9464      * @param {Function} fn the method the event invokes
9465      * @return {Roo.Element} this
9466      * @method
9467      */
9468     ep.un = ep.removeListener;
9469
9470     /**
9471      * true to automatically adjust width and height settings for box-model issues (default to true)
9472      */
9473     ep.autoBoxAdjust = true;
9474
9475     // private
9476     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9477
9478     // private
9479     El.addUnits = function(v, defaultUnit){
9480         if(v === "" || v == "auto"){
9481             return v;
9482         }
9483         if(v === undefined){
9484             return '';
9485         }
9486         if(typeof v == "number" || !El.unitPattern.test(v)){
9487             return v + (defaultUnit || 'px');
9488         }
9489         return v;
9490     };
9491
9492     // special markup used throughout Roo when box wrapping elements
9493     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>';
9494     /**
9495      * Visibility mode constant - Use visibility to hide element
9496      * @static
9497      * @type Number
9498      */
9499     El.VISIBILITY = 1;
9500     /**
9501      * Visibility mode constant - Use display to hide element
9502      * @static
9503      * @type Number
9504      */
9505     El.DISPLAY = 2;
9506
9507     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9508     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9509     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9510
9511
9512
9513     /**
9514      * @private
9515      */
9516     El.cache = {};
9517
9518     var docEl;
9519
9520     /**
9521      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9522      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9523      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9524      * @return {Element} The Element object
9525      * @static
9526      */
9527     El.get = function(el){
9528         var ex, elm, id;
9529         if(!el){ return null; }
9530         if(typeof el == "string"){ // element id
9531             if(!(elm = document.getElementById(el))){
9532                 return null;
9533             }
9534             if(ex = El.cache[el]){
9535                 ex.dom = elm;
9536             }else{
9537                 ex = El.cache[el] = new El(elm);
9538             }
9539             return ex;
9540         }else if(el.tagName){ // dom element
9541             if(!(id = el.id)){
9542                 id = Roo.id(el);
9543             }
9544             if(ex = El.cache[id]){
9545                 ex.dom = el;
9546             }else{
9547                 ex = El.cache[id] = new El(el);
9548             }
9549             return ex;
9550         }else if(el instanceof El){
9551             if(el != docEl){
9552                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9553                                                               // catch case where it hasn't been appended
9554                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9555             }
9556             return el;
9557         }else if(el.isComposite){
9558             return el;
9559         }else if(el instanceof Array){
9560             return El.select(el);
9561         }else if(el == document){
9562             // create a bogus element object representing the document object
9563             if(!docEl){
9564                 var f = function(){};
9565                 f.prototype = El.prototype;
9566                 docEl = new f();
9567                 docEl.dom = document;
9568             }
9569             return docEl;
9570         }
9571         return null;
9572     };
9573
9574     // private
9575     El.uncache = function(el){
9576         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9577             if(a[i]){
9578                 delete El.cache[a[i].id || a[i]];
9579             }
9580         }
9581     };
9582
9583     // private
9584     // Garbage collection - uncache elements/purge listeners on orphaned elements
9585     // so we don't hold a reference and cause the browser to retain them
9586     El.garbageCollect = function(){
9587         if(!Roo.enableGarbageCollector){
9588             clearInterval(El.collectorThread);
9589             return;
9590         }
9591         for(var eid in El.cache){
9592             var el = El.cache[eid], d = el.dom;
9593             // -------------------------------------------------------
9594             // Determining what is garbage:
9595             // -------------------------------------------------------
9596             // !d
9597             // dom node is null, definitely garbage
9598             // -------------------------------------------------------
9599             // !d.parentNode
9600             // no parentNode == direct orphan, definitely garbage
9601             // -------------------------------------------------------
9602             // !d.offsetParent && !document.getElementById(eid)
9603             // display none elements have no offsetParent so we will
9604             // also try to look it up by it's id. However, check
9605             // offsetParent first so we don't do unneeded lookups.
9606             // This enables collection of elements that are not orphans
9607             // directly, but somewhere up the line they have an orphan
9608             // parent.
9609             // -------------------------------------------------------
9610             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9611                 delete El.cache[eid];
9612                 if(d && Roo.enableListenerCollection){
9613                     E.purgeElement(d);
9614                 }
9615             }
9616         }
9617     }
9618     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9619
9620
9621     // dom is optional
9622     El.Flyweight = function(dom){
9623         this.dom = dom;
9624     };
9625     El.Flyweight.prototype = El.prototype;
9626
9627     El._flyweights = {};
9628     /**
9629      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9630      * the dom node can be overwritten by other code.
9631      * @param {String/HTMLElement} el The dom node or id
9632      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9633      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9634      * @static
9635      * @return {Element} The shared Element object
9636      */
9637     El.fly = function(el, named){
9638         named = named || '_global';
9639         el = Roo.getDom(el);
9640         if(!el){
9641             return null;
9642         }
9643         if(!El._flyweights[named]){
9644             El._flyweights[named] = new El.Flyweight();
9645         }
9646         El._flyweights[named].dom = el;
9647         return El._flyweights[named];
9648     };
9649
9650     /**
9651      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9652      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9653      * Shorthand of {@link Roo.Element#get}
9654      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9655      * @return {Element} The Element object
9656      * @member Roo
9657      * @method get
9658      */
9659     Roo.get = El.get;
9660     /**
9661      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9662      * the dom node can be overwritten by other code.
9663      * Shorthand of {@link Roo.Element#fly}
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      * @member Roo
9670      * @method fly
9671      */
9672     Roo.fly = El.fly;
9673
9674     // speedy lookup for elements never to box adjust
9675     var noBoxAdjust = Roo.isStrict ? {
9676         select:1
9677     } : {
9678         input:1, select:1, textarea:1
9679     };
9680     if(Roo.isIE || Roo.isGecko){
9681         noBoxAdjust['button'] = 1;
9682     }
9683
9684
9685     Roo.EventManager.on(window, 'unload', function(){
9686         delete El.cache;
9687         delete El._flyweights;
9688     });
9689 })();
9690
9691
9692
9693
9694 if(Roo.DomQuery){
9695     Roo.Element.selectorFunction = Roo.DomQuery.select;
9696 }
9697
9698 Roo.Element.select = function(selector, unique, root){
9699     var els;
9700     if(typeof selector == "string"){
9701         els = Roo.Element.selectorFunction(selector, root);
9702     }else if(selector.length !== undefined){
9703         els = selector;
9704     }else{
9705         throw "Invalid selector";
9706     }
9707     if(unique === true){
9708         return new Roo.CompositeElement(els);
9709     }else{
9710         return new Roo.CompositeElementLite(els);
9711     }
9712 };
9713 /**
9714  * Selects elements based on the passed CSS selector to enable working on them as 1.
9715  * @param {String/Array} selector The CSS selector or an array of elements
9716  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9717  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9718  * @return {CompositeElementLite/CompositeElement}
9719  * @member Roo
9720  * @method select
9721  */
9722 Roo.select = Roo.Element.select;
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737 /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749
9750 //Notifies Element that fx methods are available
9751 Roo.enableFx = true;
9752
9753 /**
9754  * @class Roo.Fx
9755  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9756  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9757  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9758  * Element effects to work.</p><br/>
9759  *
9760  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9761  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9762  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9763  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9764  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9765  * expected results and should be done with care.</p><br/>
9766  *
9767  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9768  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9769 <pre>
9770 Value  Description
9771 -----  -----------------------------
9772 tl     The top left corner
9773 t      The center of the top edge
9774 tr     The top right corner
9775 l      The center of the left edge
9776 r      The center of the right edge
9777 bl     The bottom left corner
9778 b      The center of the bottom edge
9779 br     The bottom right corner
9780 </pre>
9781  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9782  * below are common options that can be passed to any Fx method.</b>
9783  * @cfg {Function} callback A function called when the effect is finished
9784  * @cfg {Object} scope The scope of the effect function
9785  * @cfg {String} easing A valid Easing value for the effect
9786  * @cfg {String} afterCls A css class to apply after the effect
9787  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9788  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9789  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9790  * effects that end with the element being visually hidden, ignored otherwise)
9791  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9792  * a function which returns such a specification that will be applied to the Element after the effect finishes
9793  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9794  * @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
9795  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9796  */
9797 Roo.Fx = {
9798         /**
9799          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9800          * origin for the slide effect.  This function automatically handles wrapping the element with
9801          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9802          * Usage:
9803          *<pre><code>
9804 // default: slide the element in from the top
9805 el.slideIn();
9806
9807 // custom: slide the element in from the right with a 2-second duration
9808 el.slideIn('r', { duration: 2 });
9809
9810 // common config options shown with default values
9811 el.slideIn('t', {
9812     easing: 'easeOut',
9813     duration: .5
9814 });
9815 </code></pre>
9816          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9817          * @param {Object} options (optional) Object literal with any of the Fx config options
9818          * @return {Roo.Element} The Element
9819          */
9820     slideIn : function(anchor, o){
9821         var el = this.getFxEl();
9822         o = o || {};
9823
9824         el.queueFx(o, function(){
9825
9826             anchor = anchor || "t";
9827
9828             // fix display to visibility
9829             this.fixDisplay();
9830
9831             // restore values after effect
9832             var r = this.getFxRestore();
9833             var b = this.getBox();
9834             // fixed size for slide
9835             this.setSize(b);
9836
9837             // wrap if needed
9838             var wrap = this.fxWrap(r.pos, o, "hidden");
9839
9840             var st = this.dom.style;
9841             st.visibility = "visible";
9842             st.position = "absolute";
9843
9844             // clear out temp styles after slide and unwrap
9845             var after = function(){
9846                 el.fxUnwrap(wrap, r.pos, o);
9847                 st.width = r.width;
9848                 st.height = r.height;
9849                 el.afterFx(o);
9850             };
9851             // time to calc the positions
9852             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9853
9854             switch(anchor.toLowerCase()){
9855                 case "t":
9856                     wrap.setSize(b.width, 0);
9857                     st.left = st.bottom = "0";
9858                     a = {height: bh};
9859                 break;
9860                 case "l":
9861                     wrap.setSize(0, b.height);
9862                     st.right = st.top = "0";
9863                     a = {width: bw};
9864                 break;
9865                 case "r":
9866                     wrap.setSize(0, b.height);
9867                     wrap.setX(b.right);
9868                     st.left = st.top = "0";
9869                     a = {width: bw, points: pt};
9870                 break;
9871                 case "b":
9872                     wrap.setSize(b.width, 0);
9873                     wrap.setY(b.bottom);
9874                     st.left = st.top = "0";
9875                     a = {height: bh, points: pt};
9876                 break;
9877                 case "tl":
9878                     wrap.setSize(0, 0);
9879                     st.right = st.bottom = "0";
9880                     a = {width: bw, height: bh};
9881                 break;
9882                 case "bl":
9883                     wrap.setSize(0, 0);
9884                     wrap.setY(b.y+b.height);
9885                     st.right = st.top = "0";
9886                     a = {width: bw, height: bh, points: pt};
9887                 break;
9888                 case "br":
9889                     wrap.setSize(0, 0);
9890                     wrap.setXY([b.right, b.bottom]);
9891                     st.left = st.top = "0";
9892                     a = {width: bw, height: bh, points: pt};
9893                 break;
9894                 case "tr":
9895                     wrap.setSize(0, 0);
9896                     wrap.setX(b.x+b.width);
9897                     st.left = st.bottom = "0";
9898                     a = {width: bw, height: bh, points: pt};
9899                 break;
9900             }
9901             this.dom.style.visibility = "visible";
9902             wrap.show();
9903
9904             arguments.callee.anim = wrap.fxanim(a,
9905                 o,
9906                 'motion',
9907                 .5,
9908                 'easeOut', after);
9909         });
9910         return this;
9911     },
9912     
9913         /**
9914          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9915          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9916          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9917          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9918          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9919          * Usage:
9920          *<pre><code>
9921 // default: slide the element out to the top
9922 el.slideOut();
9923
9924 // custom: slide the element out to the right with a 2-second duration
9925 el.slideOut('r', { duration: 2 });
9926
9927 // common config options shown with default values
9928 el.slideOut('t', {
9929     easing: 'easeOut',
9930     duration: .5,
9931     remove: false,
9932     useDisplay: false
9933 });
9934 </code></pre>
9935          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9936          * @param {Object} options (optional) Object literal with any of the Fx config options
9937          * @return {Roo.Element} The Element
9938          */
9939     slideOut : function(anchor, o){
9940         var el = this.getFxEl();
9941         o = o || {};
9942
9943         el.queueFx(o, function(){
9944
9945             anchor = anchor || "t";
9946
9947             // restore values after effect
9948             var r = this.getFxRestore();
9949             
9950             var b = this.getBox();
9951             // fixed size for slide
9952             this.setSize(b);
9953
9954             // wrap if needed
9955             var wrap = this.fxWrap(r.pos, o, "visible");
9956
9957             var st = this.dom.style;
9958             st.visibility = "visible";
9959             st.position = "absolute";
9960
9961             wrap.setSize(b);
9962
9963             var after = function(){
9964                 if(o.useDisplay){
9965                     el.setDisplayed(false);
9966                 }else{
9967                     el.hide();
9968                 }
9969
9970                 el.fxUnwrap(wrap, r.pos, o);
9971
9972                 st.width = r.width;
9973                 st.height = r.height;
9974
9975                 el.afterFx(o);
9976             };
9977
9978             var a, zero = {to: 0};
9979             switch(anchor.toLowerCase()){
9980                 case "t":
9981                     st.left = st.bottom = "0";
9982                     a = {height: zero};
9983                 break;
9984                 case "l":
9985                     st.right = st.top = "0";
9986                     a = {width: zero};
9987                 break;
9988                 case "r":
9989                     st.left = st.top = "0";
9990                     a = {width: zero, points: {to:[b.right, b.y]}};
9991                 break;
9992                 case "b":
9993                     st.left = st.top = "0";
9994                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9995                 break;
9996                 case "tl":
9997                     st.right = st.bottom = "0";
9998                     a = {width: zero, height: zero};
9999                 break;
10000                 case "bl":
10001                     st.right = st.top = "0";
10002                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10003                 break;
10004                 case "br":
10005                     st.left = st.top = "0";
10006                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10007                 break;
10008                 case "tr":
10009                     st.left = st.bottom = "0";
10010                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10011                 break;
10012             }
10013
10014             arguments.callee.anim = wrap.fxanim(a,
10015                 o,
10016                 'motion',
10017                 .5,
10018                 "easeOut", after);
10019         });
10020         return this;
10021     },
10022
10023         /**
10024          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10025          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10026          * The element must be removed from the DOM using the 'remove' config option if desired.
10027          * Usage:
10028          *<pre><code>
10029 // default
10030 el.puff();
10031
10032 // common config options shown with default values
10033 el.puff({
10034     easing: 'easeOut',
10035     duration: .5,
10036     remove: false,
10037     useDisplay: false
10038 });
10039 </code></pre>
10040          * @param {Object} options (optional) Object literal with any of the Fx config options
10041          * @return {Roo.Element} The Element
10042          */
10043     puff : function(o){
10044         var el = this.getFxEl();
10045         o = o || {};
10046
10047         el.queueFx(o, function(){
10048             this.clearOpacity();
10049             this.show();
10050
10051             // restore values after effect
10052             var r = this.getFxRestore();
10053             var st = this.dom.style;
10054
10055             var after = function(){
10056                 if(o.useDisplay){
10057                     el.setDisplayed(false);
10058                 }else{
10059                     el.hide();
10060                 }
10061
10062                 el.clearOpacity();
10063
10064                 el.setPositioning(r.pos);
10065                 st.width = r.width;
10066                 st.height = r.height;
10067                 st.fontSize = '';
10068                 el.afterFx(o);
10069             };
10070
10071             var width = this.getWidth();
10072             var height = this.getHeight();
10073
10074             arguments.callee.anim = this.fxanim({
10075                     width : {to: this.adjustWidth(width * 2)},
10076                     height : {to: this.adjustHeight(height * 2)},
10077                     points : {by: [-(width * .5), -(height * .5)]},
10078                     opacity : {to: 0},
10079                     fontSize: {to:200, unit: "%"}
10080                 },
10081                 o,
10082                 'motion',
10083                 .5,
10084                 "easeOut", after);
10085         });
10086         return this;
10087     },
10088
10089         /**
10090          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10091          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10092          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10093          * Usage:
10094          *<pre><code>
10095 // default
10096 el.switchOff();
10097
10098 // all config options shown with default values
10099 el.switchOff({
10100     easing: 'easeIn',
10101     duration: .3,
10102     remove: false,
10103     useDisplay: false
10104 });
10105 </code></pre>
10106          * @param {Object} options (optional) Object literal with any of the Fx config options
10107          * @return {Roo.Element} The Element
10108          */
10109     switchOff : function(o){
10110         var el = this.getFxEl();
10111         o = o || {};
10112
10113         el.queueFx(o, function(){
10114             this.clearOpacity();
10115             this.clip();
10116
10117             // restore values after effect
10118             var r = this.getFxRestore();
10119             var st = this.dom.style;
10120
10121             var after = function(){
10122                 if(o.useDisplay){
10123                     el.setDisplayed(false);
10124                 }else{
10125                     el.hide();
10126                 }
10127
10128                 el.clearOpacity();
10129                 el.setPositioning(r.pos);
10130                 st.width = r.width;
10131                 st.height = r.height;
10132
10133                 el.afterFx(o);
10134             };
10135
10136             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10137                 this.clearOpacity();
10138                 (function(){
10139                     this.fxanim({
10140                         height:{to:1},
10141                         points:{by:[0, this.getHeight() * .5]}
10142                     }, o, 'motion', 0.3, 'easeIn', after);
10143                 }).defer(100, this);
10144             });
10145         });
10146         return this;
10147     },
10148
10149     /**
10150      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10151      * changed using the "attr" config option) and then fading back to the original color. If no original
10152      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10153      * Usage:
10154 <pre><code>
10155 // default: highlight background to yellow
10156 el.highlight();
10157
10158 // custom: highlight foreground text to blue for 2 seconds
10159 el.highlight("0000ff", { attr: 'color', duration: 2 });
10160
10161 // common config options shown with default values
10162 el.highlight("ffff9c", {
10163     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10164     endColor: (current color) or "ffffff",
10165     easing: 'easeIn',
10166     duration: 1
10167 });
10168 </code></pre>
10169      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10170      * @param {Object} options (optional) Object literal with any of the Fx config options
10171      * @return {Roo.Element} The Element
10172      */ 
10173     highlight : function(color, o){
10174         var el = this.getFxEl();
10175         o = o || {};
10176
10177         el.queueFx(o, function(){
10178             color = color || "ffff9c";
10179             attr = o.attr || "backgroundColor";
10180
10181             this.clearOpacity();
10182             this.show();
10183
10184             var origColor = this.getColor(attr);
10185             var restoreColor = this.dom.style[attr];
10186             endColor = (o.endColor || origColor) || "ffffff";
10187
10188             var after = function(){
10189                 el.dom.style[attr] = restoreColor;
10190                 el.afterFx(o);
10191             };
10192
10193             var a = {};
10194             a[attr] = {from: color, to: endColor};
10195             arguments.callee.anim = this.fxanim(a,
10196                 o,
10197                 'color',
10198                 1,
10199                 'easeIn', after);
10200         });
10201         return this;
10202     },
10203
10204    /**
10205     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10206     * Usage:
10207 <pre><code>
10208 // default: a single light blue ripple
10209 el.frame();
10210
10211 // custom: 3 red ripples lasting 3 seconds total
10212 el.frame("ff0000", 3, { duration: 3 });
10213
10214 // common config options shown with default values
10215 el.frame("C3DAF9", 1, {
10216     duration: 1 //duration of entire animation (not each individual ripple)
10217     // Note: Easing is not configurable and will be ignored if included
10218 });
10219 </code></pre>
10220     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10221     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10222     * @param {Object} options (optional) Object literal with any of the Fx config options
10223     * @return {Roo.Element} The Element
10224     */
10225     frame : function(color, count, o){
10226         var el = this.getFxEl();
10227         o = o || {};
10228
10229         el.queueFx(o, function(){
10230             color = color || "#C3DAF9";
10231             if(color.length == 6){
10232                 color = "#" + color;
10233             }
10234             count = count || 1;
10235             duration = o.duration || 1;
10236             this.show();
10237
10238             var b = this.getBox();
10239             var animFn = function(){
10240                 var proxy = this.createProxy({
10241
10242                      style:{
10243                         visbility:"hidden",
10244                         position:"absolute",
10245                         "z-index":"35000", // yee haw
10246                         border:"0px solid " + color
10247                      }
10248                   });
10249                 var scale = Roo.isBorderBox ? 2 : 1;
10250                 proxy.animate({
10251                     top:{from:b.y, to:b.y - 20},
10252                     left:{from:b.x, to:b.x - 20},
10253                     borderWidth:{from:0, to:10},
10254                     opacity:{from:1, to:0},
10255                     height:{from:b.height, to:(b.height + (20*scale))},
10256                     width:{from:b.width, to:(b.width + (20*scale))}
10257                 }, duration, function(){
10258                     proxy.remove();
10259                 });
10260                 if(--count > 0){
10261                      animFn.defer((duration/2)*1000, this);
10262                 }else{
10263                     el.afterFx(o);
10264                 }
10265             };
10266             animFn.call(this);
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Creates a pause before any subsequent queued effects begin.  If there are
10273     * no effects queued after the pause it will have no effect.
10274     * Usage:
10275 <pre><code>
10276 el.pause(1);
10277 </code></pre>
10278     * @param {Number} seconds The length of time to pause (in seconds)
10279     * @return {Roo.Element} The Element
10280     */
10281     pause : function(seconds){
10282         var el = this.getFxEl();
10283         var o = {};
10284
10285         el.queueFx(o, function(){
10286             setTimeout(function(){
10287                 el.afterFx(o);
10288             }, seconds * 1000);
10289         });
10290         return this;
10291     },
10292
10293    /**
10294     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10295     * using the "endOpacity" config option.
10296     * Usage:
10297 <pre><code>
10298 // default: fade in from opacity 0 to 100%
10299 el.fadeIn();
10300
10301 // custom: fade in from opacity 0 to 75% over 2 seconds
10302 el.fadeIn({ endOpacity: .75, duration: 2});
10303
10304 // common config options shown with default values
10305 el.fadeIn({
10306     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10307     easing: 'easeOut',
10308     duration: .5
10309 });
10310 </code></pre>
10311     * @param {Object} options (optional) Object literal with any of the Fx config options
10312     * @return {Roo.Element} The Element
10313     */
10314     fadeIn : function(o){
10315         var el = this.getFxEl();
10316         o = o || {};
10317         el.queueFx(o, function(){
10318             this.setOpacity(0);
10319             this.fixDisplay();
10320             this.dom.style.visibility = 'visible';
10321             var to = o.endOpacity || 1;
10322             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10323                 o, null, .5, "easeOut", function(){
10324                 if(to == 1){
10325                     this.clearOpacity();
10326                 }
10327                 el.afterFx(o);
10328             });
10329         });
10330         return this;
10331     },
10332
10333    /**
10334     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10335     * using the "endOpacity" config option.
10336     * Usage:
10337 <pre><code>
10338 // default: fade out from the element's current opacity to 0
10339 el.fadeOut();
10340
10341 // custom: fade out from the element's current opacity to 25% over 2 seconds
10342 el.fadeOut({ endOpacity: .25, duration: 2});
10343
10344 // common config options shown with default values
10345 el.fadeOut({
10346     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10347     easing: 'easeOut',
10348     duration: .5
10349     remove: false,
10350     useDisplay: false
10351 });
10352 </code></pre>
10353     * @param {Object} options (optional) Object literal with any of the Fx config options
10354     * @return {Roo.Element} The Element
10355     */
10356     fadeOut : function(o){
10357         var el = this.getFxEl();
10358         o = o || {};
10359         el.queueFx(o, function(){
10360             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10361                 o, null, .5, "easeOut", function(){
10362                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10363                      this.dom.style.display = "none";
10364                 }else{
10365                      this.dom.style.visibility = "hidden";
10366                 }
10367                 this.clearOpacity();
10368                 el.afterFx(o);
10369             });
10370         });
10371         return this;
10372     },
10373
10374    /**
10375     * Animates the transition of an element's dimensions from a starting height/width
10376     * to an ending height/width.
10377     * Usage:
10378 <pre><code>
10379 // change height and width to 100x100 pixels
10380 el.scale(100, 100);
10381
10382 // common config options shown with default values.  The height and width will default to
10383 // the element's existing values if passed as null.
10384 el.scale(
10385     [element's width],
10386     [element's height], {
10387     easing: 'easeOut',
10388     duration: .35
10389 });
10390 </code></pre>
10391     * @param {Number} width  The new width (pass undefined to keep the original width)
10392     * @param {Number} height  The new height (pass undefined to keep the original height)
10393     * @param {Object} options (optional) Object literal with any of the Fx config options
10394     * @return {Roo.Element} The Element
10395     */
10396     scale : function(w, h, o){
10397         this.shift(Roo.apply({}, o, {
10398             width: w,
10399             height: h
10400         }));
10401         return this;
10402     },
10403
10404    /**
10405     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10406     * Any of these properties not specified in the config object will not be changed.  This effect 
10407     * requires that at least one new dimension, position or opacity setting must be passed in on
10408     * the config object in order for the function to have any effect.
10409     * Usage:
10410 <pre><code>
10411 // slide the element horizontally to x position 200 while changing the height and opacity
10412 el.shift({ x: 200, height: 50, opacity: .8 });
10413
10414 // common config options shown with default values.
10415 el.shift({
10416     width: [element's width],
10417     height: [element's height],
10418     x: [element's x position],
10419     y: [element's y position],
10420     opacity: [element's opacity],
10421     easing: 'easeOut',
10422     duration: .35
10423 });
10424 </code></pre>
10425     * @param {Object} options  Object literal with any of the Fx config options
10426     * @return {Roo.Element} The Element
10427     */
10428     shift : function(o){
10429         var el = this.getFxEl();
10430         o = o || {};
10431         el.queueFx(o, function(){
10432             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10433             if(w !== undefined){
10434                 a.width = {to: this.adjustWidth(w)};
10435             }
10436             if(h !== undefined){
10437                 a.height = {to: this.adjustHeight(h)};
10438             }
10439             if(x !== undefined || y !== undefined){
10440                 a.points = {to: [
10441                     x !== undefined ? x : this.getX(),
10442                     y !== undefined ? y : this.getY()
10443                 ]};
10444             }
10445             if(op !== undefined){
10446                 a.opacity = {to: op};
10447             }
10448             if(o.xy !== undefined){
10449                 a.points = {to: o.xy};
10450             }
10451             arguments.callee.anim = this.fxanim(a,
10452                 o, 'motion', .35, "easeOut", function(){
10453                 el.afterFx(o);
10454             });
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10461          * ending point of the effect.
10462          * Usage:
10463          *<pre><code>
10464 // default: slide the element downward while fading out
10465 el.ghost();
10466
10467 // custom: slide the element out to the right with a 2-second duration
10468 el.ghost('r', { duration: 2 });
10469
10470 // common config options shown with default values
10471 el.ghost('b', {
10472     easing: 'easeOut',
10473     duration: .5
10474     remove: false,
10475     useDisplay: false
10476 });
10477 </code></pre>
10478          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10479          * @param {Object} options (optional) Object literal with any of the Fx config options
10480          * @return {Roo.Element} The Element
10481          */
10482     ghost : function(anchor, o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485
10486         el.queueFx(o, function(){
10487             anchor = anchor || "b";
10488
10489             // restore values after effect
10490             var r = this.getFxRestore();
10491             var w = this.getWidth(),
10492                 h = this.getHeight();
10493
10494             var st = this.dom.style;
10495
10496             var after = function(){
10497                 if(o.useDisplay){
10498                     el.setDisplayed(false);
10499                 }else{
10500                     el.hide();
10501                 }
10502
10503                 el.clearOpacity();
10504                 el.setPositioning(r.pos);
10505                 st.width = r.width;
10506                 st.height = r.height;
10507
10508                 el.afterFx(o);
10509             };
10510
10511             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10512             switch(anchor.toLowerCase()){
10513                 case "t":
10514                     pt.by = [0, -h];
10515                 break;
10516                 case "l":
10517                     pt.by = [-w, 0];
10518                 break;
10519                 case "r":
10520                     pt.by = [w, 0];
10521                 break;
10522                 case "b":
10523                     pt.by = [0, h];
10524                 break;
10525                 case "tl":
10526                     pt.by = [-w, -h];
10527                 break;
10528                 case "bl":
10529                     pt.by = [-w, h];
10530                 break;
10531                 case "br":
10532                     pt.by = [w, h];
10533                 break;
10534                 case "tr":
10535                     pt.by = [w, -h];
10536                 break;
10537             }
10538
10539             arguments.callee.anim = this.fxanim(a,
10540                 o,
10541                 'motion',
10542                 .5,
10543                 "easeOut", after);
10544         });
10545         return this;
10546     },
10547
10548         /**
10549          * Ensures that all effects queued after syncFx is called on the element are
10550          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10551          * @return {Roo.Element} The Element
10552          */
10553     syncFx : function(){
10554         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10555             block : false,
10556             concurrent : true,
10557             stopFx : false
10558         });
10559         return this;
10560     },
10561
10562         /**
10563          * Ensures that all effects queued after sequenceFx is called on the element are
10564          * run in sequence.  This is the opposite of {@link #syncFx}.
10565          * @return {Roo.Element} The Element
10566          */
10567     sequenceFx : function(){
10568         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10569             block : false,
10570             concurrent : false,
10571             stopFx : false
10572         });
10573         return this;
10574     },
10575
10576         /* @private */
10577     nextFx : function(){
10578         var ef = this.fxQueue[0];
10579         if(ef){
10580             ef.call(this);
10581         }
10582     },
10583
10584         /**
10585          * Returns true if the element has any effects actively running or queued, else returns false.
10586          * @return {Boolean} True if element has active effects, else false
10587          */
10588     hasActiveFx : function(){
10589         return this.fxQueue && this.fxQueue[0];
10590     },
10591
10592         /**
10593          * Stops any running effects and clears the element's internal effects queue if it contains
10594          * any additional effects that haven't started yet.
10595          * @return {Roo.Element} The Element
10596          */
10597     stopFx : function(){
10598         if(this.hasActiveFx()){
10599             var cur = this.fxQueue[0];
10600             if(cur && cur.anim && cur.anim.isAnimated()){
10601                 this.fxQueue = [cur]; // clear out others
10602                 cur.anim.stop(true);
10603             }
10604         }
10605         return this;
10606     },
10607
10608         /* @private */
10609     beforeFx : function(o){
10610         if(this.hasActiveFx() && !o.concurrent){
10611            if(o.stopFx){
10612                this.stopFx();
10613                return true;
10614            }
10615            return false;
10616         }
10617         return true;
10618     },
10619
10620         /**
10621          * Returns true if the element is currently blocking so that no other effect can be queued
10622          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10623          * used to ensure that an effect initiated by a user action runs to completion prior to the
10624          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10625          * @return {Boolean} True if blocking, else false
10626          */
10627     hasFxBlock : function(){
10628         var q = this.fxQueue;
10629         return q && q[0] && q[0].block;
10630     },
10631
10632         /* @private */
10633     queueFx : function(o, fn){
10634         if(!this.fxQueue){
10635             this.fxQueue = [];
10636         }
10637         if(!this.hasFxBlock()){
10638             Roo.applyIf(o, this.fxDefaults);
10639             if(!o.concurrent){
10640                 var run = this.beforeFx(o);
10641                 fn.block = o.block;
10642                 this.fxQueue.push(fn);
10643                 if(run){
10644                     this.nextFx();
10645                 }
10646             }else{
10647                 fn.call(this);
10648             }
10649         }
10650         return this;
10651     },
10652
10653         /* @private */
10654     fxWrap : function(pos, o, vis){
10655         var wrap;
10656         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10657             var wrapXY;
10658             if(o.fixPosition){
10659                 wrapXY = this.getXY();
10660             }
10661             var div = document.createElement("div");
10662             div.style.visibility = vis;
10663             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10664             wrap.setPositioning(pos);
10665             if(wrap.getStyle("position") == "static"){
10666                 wrap.position("relative");
10667             }
10668             this.clearPositioning('auto');
10669             wrap.clip();
10670             wrap.dom.appendChild(this.dom);
10671             if(wrapXY){
10672                 wrap.setXY(wrapXY);
10673             }
10674         }
10675         return wrap;
10676     },
10677
10678         /* @private */
10679     fxUnwrap : function(wrap, pos, o){
10680         this.clearPositioning();
10681         this.setPositioning(pos);
10682         if(!o.wrap){
10683             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10684             wrap.remove();
10685         }
10686     },
10687
10688         /* @private */
10689     getFxRestore : function(){
10690         var st = this.dom.style;
10691         return {pos: this.getPositioning(), width: st.width, height : st.height};
10692     },
10693
10694         /* @private */
10695     afterFx : function(o){
10696         if(o.afterStyle){
10697             this.applyStyles(o.afterStyle);
10698         }
10699         if(o.afterCls){
10700             this.addClass(o.afterCls);
10701         }
10702         if(o.remove === true){
10703             this.remove();
10704         }
10705         Roo.callback(o.callback, o.scope, [this]);
10706         if(!o.concurrent){
10707             this.fxQueue.shift();
10708             this.nextFx();
10709         }
10710     },
10711
10712         /* @private */
10713     getFxEl : function(){ // support for composite element fx
10714         return Roo.get(this.dom);
10715     },
10716
10717         /* @private */
10718     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10719         animType = animType || 'run';
10720         opt = opt || {};
10721         var anim = Roo.lib.Anim[animType](
10722             this.dom, args,
10723             (opt.duration || defaultDur) || .35,
10724             (opt.easing || defaultEase) || 'easeOut',
10725             function(){
10726                 Roo.callback(cb, this);
10727             },
10728             this
10729         );
10730         opt.anim = anim;
10731         return anim;
10732     }
10733 };
10734
10735 // backwords compat
10736 Roo.Fx.resize = Roo.Fx.scale;
10737
10738 //When included, Roo.Fx is automatically applied to Element so that all basic
10739 //effects are available directly via the Element API
10740 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751
10752 /**
10753  * @class Roo.CompositeElement
10754  * Standard composite class. Creates a Roo.Element for every element in the collection.
10755  * <br><br>
10756  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10757  * actions will be performed on all the elements in this collection.</b>
10758  * <br><br>
10759  * All methods return <i>this</i> and can be chained.
10760  <pre><code>
10761  var els = Roo.select("#some-el div.some-class", true);
10762  // or select directly from an existing element
10763  var el = Roo.get('some-el');
10764  el.select('div.some-class', true);
10765
10766  els.setWidth(100); // all elements become 100 width
10767  els.hide(true); // all elements fade out and hide
10768  // or
10769  els.setWidth(100).hide(true);
10770  </code></pre>
10771  */
10772 Roo.CompositeElement = function(els){
10773     this.elements = [];
10774     this.addElements(els);
10775 };
10776 Roo.CompositeElement.prototype = {
10777     isComposite: true,
10778     addElements : function(els){
10779         if(!els) return this;
10780         if(typeof els == "string"){
10781             els = Roo.Element.selectorFunction(els);
10782         }
10783         var yels = this.elements;
10784         var index = yels.length-1;
10785         for(var i = 0, len = els.length; i < len; i++) {
10786                 yels[++index] = Roo.get(els[i]);
10787         }
10788         return this;
10789     },
10790
10791     /**
10792     * Clears this composite and adds the elements returned by the passed selector.
10793     * @param {String/Array} els A string CSS selector, an array of elements or an element
10794     * @return {CompositeElement} this
10795     */
10796     fill : function(els){
10797         this.elements = [];
10798         this.add(els);
10799         return this;
10800     },
10801
10802     /**
10803     * Filters this composite to only elements that match the passed selector.
10804     * @param {String} selector A string CSS selector
10805     * @return {CompositeElement} this
10806     */
10807     filter : function(selector){
10808         var els = [];
10809         this.each(function(el){
10810             if(el.is(selector)){
10811                 els[els.length] = el.dom;
10812             }
10813         });
10814         this.fill(els);
10815         return this;
10816     },
10817
10818     invoke : function(fn, args){
10819         var els = this.elements;
10820         for(var i = 0, len = els.length; i < len; i++) {
10821                 Roo.Element.prototype[fn].apply(els[i], args);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Adds elements to this composite.
10827     * @param {String/Array} els A string CSS selector, an array of elements or an element
10828     * @return {CompositeElement} this
10829     */
10830     add : function(els){
10831         if(typeof els == "string"){
10832             this.addElements(Roo.Element.selectorFunction(els));
10833         }else if(els.length !== undefined){
10834             this.addElements(els);
10835         }else{
10836             this.addElements([els]);
10837         }
10838         return this;
10839     },
10840     /**
10841     * Calls the passed function passing (el, this, index) for each element in this composite.
10842     * @param {Function} fn The function to call
10843     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10844     * @return {CompositeElement} this
10845     */
10846     each : function(fn, scope){
10847         var els = this.elements;
10848         for(var i = 0, len = els.length; i < len; i++){
10849             if(fn.call(scope || els[i], els[i], this, i) === false) {
10850                 break;
10851             }
10852         }
10853         return this;
10854     },
10855
10856     /**
10857      * Returns the Element object at the specified index
10858      * @param {Number} index
10859      * @return {Roo.Element}
10860      */
10861     item : function(index){
10862         return this.elements[index] || null;
10863     },
10864
10865     /**
10866      * Returns the first Element
10867      * @return {Roo.Element}
10868      */
10869     first : function(){
10870         return this.item(0);
10871     },
10872
10873     /**
10874      * Returns the last Element
10875      * @return {Roo.Element}
10876      */
10877     last : function(){
10878         return this.item(this.elements.length-1);
10879     },
10880
10881     /**
10882      * Returns the number of elements in this composite
10883      * @return Number
10884      */
10885     getCount : function(){
10886         return this.elements.length;
10887     },
10888
10889     /**
10890      * Returns true if this composite contains the passed element
10891      * @return Boolean
10892      */
10893     contains : function(el){
10894         return this.indexOf(el) !== -1;
10895     },
10896
10897     /**
10898      * Returns true if this composite contains the passed element
10899      * @return Boolean
10900      */
10901     indexOf : function(el){
10902         return this.elements.indexOf(Roo.get(el));
10903     },
10904
10905
10906     /**
10907     * Removes the specified element(s).
10908     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10909     * or an array of any of those.
10910     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10911     * @return {CompositeElement} this
10912     */
10913     removeElement : function(el, removeDom){
10914         if(el instanceof Array){
10915             for(var i = 0, len = el.length; i < len; i++){
10916                 this.removeElement(el[i]);
10917             }
10918             return this;
10919         }
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             if(removeDom){
10923                 var d = this.elements[index];
10924                 if(d.dom){
10925                     d.remove();
10926                 }else{
10927                     d.parentNode.removeChild(d);
10928                 }
10929             }
10930             this.elements.splice(index, 1);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Replaces the specified element with the passed element.
10937     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10938     * to replace.
10939     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10940     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10941     * @return {CompositeElement} this
10942     */
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             if(domReplace){
10947                 this.elements[index].replaceWith(replacement);
10948             }else{
10949                 this.elements.splice(index, 1, Roo.get(replacement))
10950             }
10951         }
10952         return this;
10953     },
10954
10955     /**
10956      * Removes all elements.
10957      */
10958     clear : function(){
10959         this.elements = [];
10960     }
10961 };
10962 (function(){
10963     Roo.CompositeElement.createCall = function(proto, fnName){
10964         if(!proto[fnName]){
10965             proto[fnName] = function(){
10966                 return this.invoke(fnName, arguments);
10967             };
10968         }
10969     };
10970     for(var fnName in Roo.Element.prototype){
10971         if(typeof Roo.Element.prototype[fnName] == "function"){
10972             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10973         }
10974     };
10975 })();
10976 /*
10977  * Based on:
10978  * Ext JS Library 1.1.1
10979  * Copyright(c) 2006-2007, Ext JS, LLC.
10980  *
10981  * Originally Released Under LGPL - original licence link has changed is not relivant.
10982  *
10983  * Fork - LGPL
10984  * <script type="text/javascript">
10985  */
10986
10987 /**
10988  * @class Roo.CompositeElementLite
10989  * @extends Roo.CompositeElement
10990  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10991  <pre><code>
10992  var els = Roo.select("#some-el div.some-class");
10993  // or select directly from an existing element
10994  var el = Roo.get('some-el');
10995  el.select('div.some-class');
10996
10997  els.setWidth(100); // all elements become 100 width
10998  els.hide(true); // all elements fade out and hide
10999  // or
11000  els.setWidth(100).hide(true);
11001  </code></pre><br><br>
11002  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11003  * actions will be performed on all the elements in this collection.</b>
11004  */
11005 Roo.CompositeElementLite = function(els){
11006     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11007     this.el = new Roo.Element.Flyweight();
11008 };
11009 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11010     addElements : function(els){
11011         if(els){
11012             if(els instanceof Array){
11013                 this.elements = this.elements.concat(els);
11014             }else{
11015                 var yels = this.elements;
11016                 var index = yels.length-1;
11017                 for(var i = 0, len = els.length; i < len; i++) {
11018                     yels[++index] = els[i];
11019                 }
11020             }
11021         }
11022         return this;
11023     },
11024     invoke : function(fn, args){
11025         var els = this.elements;
11026         var el = this.el;
11027         for(var i = 0, len = els.length; i < len; i++) {
11028             el.dom = els[i];
11029                 Roo.Element.prototype[fn].apply(el, args);
11030         }
11031         return this;
11032     },
11033     /**
11034      * Returns a flyweight Element of the dom element object at the specified index
11035      * @param {Number} index
11036      * @return {Roo.Element}
11037      */
11038     item : function(index){
11039         if(!this.elements[index]){
11040             return null;
11041         }
11042         this.el.dom = this.elements[index];
11043         return this.el;
11044     },
11045
11046     // fixes scope with flyweight
11047     addListener : function(eventName, handler, scope, opt){
11048         var els = this.elements;
11049         for(var i = 0, len = els.length; i < len; i++) {
11050             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11051         }
11052         return this;
11053     },
11054
11055     /**
11056     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11057     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11058     * a reference to the dom node, use el.dom.</b>
11059     * @param {Function} fn The function to call
11060     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11061     * @return {CompositeElement} this
11062     */
11063     each : function(fn, scope){
11064         var els = this.elements;
11065         var el = this.el;
11066         for(var i = 0, len = els.length; i < len; i++){
11067             el.dom = els[i];
11068                 if(fn.call(scope || el, el, this, i) === false){
11069                 break;
11070             }
11071         }
11072         return this;
11073     },
11074
11075     indexOf : function(el){
11076         return this.elements.indexOf(Roo.getDom(el));
11077     },
11078
11079     replaceElement : function(el, replacement, domReplace){
11080         var index = typeof el == 'number' ? el : this.indexOf(el);
11081         if(index !== -1){
11082             replacement = Roo.getDom(replacement);
11083             if(domReplace){
11084                 var d = this.elements[index];
11085                 d.parentNode.insertBefore(replacement, d);
11086                 d.parentNode.removeChild(d);
11087             }
11088             this.elements.splice(index, 1, replacement);
11089         }
11090         return this;
11091     }
11092 });
11093 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11094
11095 /*
11096  * Based on:
11097  * Ext JS Library 1.1.1
11098  * Copyright(c) 2006-2007, Ext JS, LLC.
11099  *
11100  * Originally Released Under LGPL - original licence link has changed is not relivant.
11101  *
11102  * Fork - LGPL
11103  * <script type="text/javascript">
11104  */
11105
11106  
11107
11108 /**
11109  * @class Roo.data.Connection
11110  * @extends Roo.util.Observable
11111  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11112  * either to a configured URL, or to a URL specified at request time.<br><br>
11113  * <p>
11114  * Requests made by this class are asynchronous, and will return immediately. No data from
11115  * the server will be available to the statement immediately following the {@link #request} call.
11116  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11117  * <p>
11118  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11119  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11120  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11121  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11122  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11123  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11124  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11125  * standard DOM methods.
11126  * @constructor
11127  * @param {Object} config a configuration object.
11128  */
11129 Roo.data.Connection = function(config){
11130     Roo.apply(this, config);
11131     this.addEvents({
11132         /**
11133          * @event beforerequest
11134          * Fires before a network request is made to retrieve a data object.
11135          * @param {Connection} conn This Connection object.
11136          * @param {Object} options The options config object passed to the {@link #request} method.
11137          */
11138         "beforerequest" : true,
11139         /**
11140          * @event requestcomplete
11141          * Fires if the request was successfully completed.
11142          * @param {Connection} conn This Connection object.
11143          * @param {Object} response The XHR object containing the response data.
11144          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11145          * @param {Object} options The options config object passed to the {@link #request} method.
11146          */
11147         "requestcomplete" : true,
11148         /**
11149          * @event requestexception
11150          * Fires if an error HTTP status was returned from the server.
11151          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11152          * @param {Connection} conn This Connection object.
11153          * @param {Object} response The XHR object containing the response data.
11154          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11155          * @param {Object} options The options config object passed to the {@link #request} method.
11156          */
11157         "requestexception" : true
11158     });
11159     Roo.data.Connection.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11163     /**
11164      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11165      */
11166     /**
11167      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11168      * extra parameters to each request made by this object. (defaults to undefined)
11169      */
11170     /**
11171      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11172      *  to each request made by this object. (defaults to undefined)
11173      */
11174     /**
11175      * @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)
11176      */
11177     /**
11178      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11179      */
11180     timeout : 30000,
11181     /**
11182      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11183      * @type Boolean
11184      */
11185     autoAbort:false,
11186
11187     /**
11188      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11189      * @type Boolean
11190      */
11191     disableCaching: true,
11192
11193     /**
11194      * Sends an HTTP request to a remote server.
11195      * @param {Object} options An object which may contain the following properties:<ul>
11196      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11197      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11198      * request, a url encoded string or a function to call to get either.</li>
11199      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11200      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11201      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11202      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11203      * <li>options {Object} The parameter to the request call.</li>
11204      * <li>success {Boolean} True if the request succeeded.</li>
11205      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11206      * </ul></li>
11207      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11208      * The callback is passed the following parameters:<ul>
11209      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11210      * <li>options {Object} The parameter to the request call.</li>
11211      * </ul></li>
11212      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11213      * The callback is passed the following parameters:<ul>
11214      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11215      * <li>options {Object} The parameter to the request call.</li>
11216      * </ul></li>
11217      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11218      * for the callback function. Defaults to the browser window.</li>
11219      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11220      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11221      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11222      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11223      * params for the post data. Any params will be appended to the URL.</li>
11224      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11225      * </ul>
11226      * @return {Number} transactionId
11227      */
11228     request : function(o){
11229         if(this.fireEvent("beforerequest", this, o) !== false){
11230             var p = o.params;
11231
11232             if(typeof p == "function"){
11233                 p = p.call(o.scope||window, o);
11234             }
11235             if(typeof p == "object"){
11236                 p = Roo.urlEncode(o.params);
11237             }
11238             if(this.extraParams){
11239                 var extras = Roo.urlEncode(this.extraParams);
11240                 p = p ? (p + '&' + extras) : extras;
11241             }
11242
11243             var url = o.url || this.url;
11244             if(typeof url == 'function'){
11245                 url = url.call(o.scope||window, o);
11246             }
11247
11248             if(o.form){
11249                 var form = Roo.getDom(o.form);
11250                 url = url || form.action;
11251
11252                 var enctype = form.getAttribute("enctype");
11253                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11254                     return this.doFormUpload(o, p, url);
11255                 }
11256                 var f = Roo.lib.Ajax.serializeForm(form);
11257                 p = p ? (p + '&' + f) : f;
11258             }
11259
11260             var hs = o.headers;
11261             if(this.defaultHeaders){
11262                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11263                 if(!o.headers){
11264                     o.headers = hs;
11265                 }
11266             }
11267
11268             var cb = {
11269                 success: this.handleResponse,
11270                 failure: this.handleFailure,
11271                 scope: this,
11272                 argument: {options: o},
11273                 timeout : this.timeout
11274             };
11275
11276             var method = o.method||this.method||(p ? "POST" : "GET");
11277
11278             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11279                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11280             }
11281
11282             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11283                 if(o.autoAbort){
11284                     this.abort();
11285                 }
11286             }else if(this.autoAbort !== false){
11287                 this.abort();
11288             }
11289
11290             if((method == 'GET' && p) || o.xmlData){
11291                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11292                 p = '';
11293             }
11294             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11295             return this.transId;
11296         }else{
11297             Roo.callback(o.callback, o.scope, [o, null, null]);
11298             return null;
11299         }
11300     },
11301
11302     /**
11303      * Determine whether this object has a request outstanding.
11304      * @param {Number} transactionId (Optional) defaults to the last transaction
11305      * @return {Boolean} True if there is an outstanding request.
11306      */
11307     isLoading : function(transId){
11308         if(transId){
11309             return Roo.lib.Ajax.isCallInProgress(transId);
11310         }else{
11311             return this.transId ? true : false;
11312         }
11313     },
11314
11315     /**
11316      * Aborts any outstanding request.
11317      * @param {Number} transactionId (Optional) defaults to the last transaction
11318      */
11319     abort : function(transId){
11320         if(transId || this.isLoading()){
11321             Roo.lib.Ajax.abort(transId || this.transId);
11322         }
11323     },
11324
11325     // private
11326     handleResponse : function(response){
11327         this.transId = false;
11328         var options = response.argument.options;
11329         response.argument = options ? options.argument : null;
11330         this.fireEvent("requestcomplete", this, response, options);
11331         Roo.callback(options.success, options.scope, [response, options]);
11332         Roo.callback(options.callback, options.scope, [options, true, response]);
11333     },
11334
11335     // private
11336     handleFailure : function(response, e){
11337         this.transId = false;
11338         var options = response.argument.options;
11339         response.argument = options ? options.argument : null;
11340         this.fireEvent("requestexception", this, response, options, e);
11341         Roo.callback(options.failure, options.scope, [response, options]);
11342         Roo.callback(options.callback, options.scope, [options, false, response]);
11343     },
11344
11345     // private
11346     doFormUpload : function(o, ps, url){
11347         var id = Roo.id();
11348         var frame = document.createElement('iframe');
11349         frame.id = id;
11350         frame.name = id;
11351         frame.className = 'x-hidden';
11352         if(Roo.isIE){
11353             frame.src = Roo.SSL_SECURE_URL;
11354         }
11355         document.body.appendChild(frame);
11356
11357         if(Roo.isIE){
11358            document.frames[id].name = id;
11359         }
11360
11361         var form = Roo.getDom(o.form);
11362         form.target = id;
11363         form.method = 'POST';
11364         form.enctype = form.encoding = 'multipart/form-data';
11365         if(url){
11366             form.action = url;
11367         }
11368
11369         var hiddens, hd;
11370         if(ps){ // add dynamic params
11371             hiddens = [];
11372             ps = Roo.urlDecode(ps, false);
11373             for(var k in ps){
11374                 if(ps.hasOwnProperty(k)){
11375                     hd = document.createElement('input');
11376                     hd.type = 'hidden';
11377                     hd.name = k;
11378                     hd.value = ps[k];
11379                     form.appendChild(hd);
11380                     hiddens.push(hd);
11381                 }
11382             }
11383         }
11384
11385         function cb(){
11386             var r = {  // bogus response object
11387                 responseText : '',
11388                 responseXML : null
11389             };
11390
11391             r.argument = o ? o.argument : null;
11392
11393             try { //
11394                 var doc;
11395                 if(Roo.isIE){
11396                     doc = frame.contentWindow.document;
11397                 }else {
11398                     doc = (frame.contentDocument || window.frames[id].document);
11399                 }
11400                 if(doc && doc.body){
11401                     r.responseText = doc.body.innerHTML;
11402                 }
11403                 if(doc && doc.XMLDocument){
11404                     r.responseXML = doc.XMLDocument;
11405                 }else {
11406                     r.responseXML = doc;
11407                 }
11408             }
11409             catch(e) {
11410                 // ignore
11411             }
11412
11413             Roo.EventManager.removeListener(frame, 'load', cb, this);
11414
11415             this.fireEvent("requestcomplete", this, r, o);
11416             Roo.callback(o.success, o.scope, [r, o]);
11417             Roo.callback(o.callback, o.scope, [o, true, r]);
11418
11419             setTimeout(function(){document.body.removeChild(frame);}, 100);
11420         }
11421
11422         Roo.EventManager.on(frame, 'load', cb, this);
11423         form.submit();
11424
11425         if(hiddens){ // remove dynamic params
11426             for(var i = 0, len = hiddens.length; i < len; i++){
11427                 form.removeChild(hiddens[i]);
11428             }
11429         }
11430     }
11431 });
11432
11433 /**
11434  * @class Roo.Ajax
11435  * @extends Roo.data.Connection
11436  * Global Ajax request class.
11437  *
11438  * @singleton
11439  */
11440 Roo.Ajax = new Roo.data.Connection({
11441     // fix up the docs
11442    /**
11443      * @cfg {String} url @hide
11444      */
11445     /**
11446      * @cfg {Object} extraParams @hide
11447      */
11448     /**
11449      * @cfg {Object} defaultHeaders @hide
11450      */
11451     /**
11452      * @cfg {String} method (Optional) @hide
11453      */
11454     /**
11455      * @cfg {Number} timeout (Optional) @hide
11456      */
11457     /**
11458      * @cfg {Boolean} autoAbort (Optional) @hide
11459      */
11460
11461     /**
11462      * @cfg {Boolean} disableCaching (Optional) @hide
11463      */
11464
11465     /**
11466      * @property  disableCaching
11467      * True to add a unique cache-buster param to GET requests. (defaults to true)
11468      * @type Boolean
11469      */
11470     /**
11471      * @property  url
11472      * The default URL to be used for requests to the server. (defaults to undefined)
11473      * @type String
11474      */
11475     /**
11476      * @property  extraParams
11477      * An object containing properties which are used as
11478      * extra parameters to each request made by this object. (defaults to undefined)
11479      * @type Object
11480      */
11481     /**
11482      * @property  defaultHeaders
11483      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11484      * @type Object
11485      */
11486     /**
11487      * @property  method
11488      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11489      * @type String
11490      */
11491     /**
11492      * @property  timeout
11493      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11494      * @type Number
11495      */
11496
11497     /**
11498      * @property  autoAbort
11499      * Whether a new request should abort any pending requests. (defaults to false)
11500      * @type Boolean
11501      */
11502     autoAbort : false,
11503
11504     /**
11505      * Serialize the passed form into a url encoded string
11506      * @param {String/HTMLElement} form
11507      * @return {String}
11508      */
11509     serializeForm : function(form){
11510         return Roo.lib.Ajax.serializeForm(form);
11511     }
11512 });/*
11513  * Based on:
11514  * Ext JS Library 1.1.1
11515  * Copyright(c) 2006-2007, Ext JS, LLC.
11516  *
11517  * Originally Released Under LGPL - original licence link has changed is not relivant.
11518  *
11519  * Fork - LGPL
11520  * <script type="text/javascript">
11521  */
11522  
11523 /**
11524  * @class Roo.Ajax
11525  * @extends Roo.data.Connection
11526  * Global Ajax request class.
11527  *
11528  * @instanceOf  Roo.data.Connection
11529  */
11530 Roo.Ajax = new Roo.data.Connection({
11531     // fix up the docs
11532     
11533     /**
11534      * fix up scoping
11535      * @scope Roo.Ajax
11536      */
11537     
11538    /**
11539      * @cfg {String} url @hide
11540      */
11541     /**
11542      * @cfg {Object} extraParams @hide
11543      */
11544     /**
11545      * @cfg {Object} defaultHeaders @hide
11546      */
11547     /**
11548      * @cfg {String} method (Optional) @hide
11549      */
11550     /**
11551      * @cfg {Number} timeout (Optional) @hide
11552      */
11553     /**
11554      * @cfg {Boolean} autoAbort (Optional) @hide
11555      */
11556
11557     /**
11558      * @cfg {Boolean} disableCaching (Optional) @hide
11559      */
11560
11561     /**
11562      * @property  disableCaching
11563      * True to add a unique cache-buster param to GET requests. (defaults to true)
11564      * @type Boolean
11565      */
11566     /**
11567      * @property  url
11568      * The default URL to be used for requests to the server. (defaults to undefined)
11569      * @type String
11570      */
11571     /**
11572      * @property  extraParams
11573      * An object containing properties which are used as
11574      * extra parameters to each request made by this object. (defaults to undefined)
11575      * @type Object
11576      */
11577     /**
11578      * @property  defaultHeaders
11579      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11580      * @type Object
11581      */
11582     /**
11583      * @property  method
11584      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11585      * @type String
11586      */
11587     /**
11588      * @property  timeout
11589      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11590      * @type Number
11591      */
11592
11593     /**
11594      * @property  autoAbort
11595      * Whether a new request should abort any pending requests. (defaults to false)
11596      * @type Boolean
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @param {String/HTMLElement} form
11603      * @return {String}
11604      */
11605     serializeForm : function(form){
11606         return Roo.lib.Ajax.serializeForm(form);
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620 /**
11621  * @class Roo.UpdateManager
11622  * @extends Roo.util.Observable
11623  * Provides AJAX-style update for Element object.<br><br>
11624  * Usage:<br>
11625  * <pre><code>
11626  * // Get it from a Roo.Element object
11627  * var el = Roo.get("foo");
11628  * var mgr = el.getUpdateManager();
11629  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11630  * ...
11631  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11632  * <br>
11633  * // or directly (returns the same UpdateManager instance)
11634  * var mgr = new Roo.UpdateManager("myElementId");
11635  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11636  * mgr.on("update", myFcnNeedsToKnow);
11637  * <br>
11638    // short handed call directly from the element object
11639    Roo.get("foo").load({
11640         url: "bar.php",
11641         scripts:true,
11642         params: "for=bar",
11643         text: "Loading Foo..."
11644    });
11645  * </code></pre>
11646  * @constructor
11647  * Create new UpdateManager directly.
11648  * @param {String/HTMLElement/Roo.Element} el The element to update
11649  * @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).
11650  */
11651 Roo.UpdateManager = function(el, forceNew){
11652     el = Roo.get(el);
11653     if(!forceNew && el.updateManager){
11654         return el.updateManager;
11655     }
11656     /**
11657      * The Element object
11658      * @type Roo.Element
11659      */
11660     this.el = el;
11661     /**
11662      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11663      * @type String
11664      */
11665     this.defaultUrl = null;
11666
11667     this.addEvents({
11668         /**
11669          * @event beforeupdate
11670          * Fired before an update is made, return false from your handler and the update is cancelled.
11671          * @param {Roo.Element} el
11672          * @param {String/Object/Function} url
11673          * @param {String/Object} params
11674          */
11675         "beforeupdate": true,
11676         /**
11677          * @event update
11678          * Fired after successful update is made.
11679          * @param {Roo.Element} el
11680          * @param {Object} oResponseObject The response Object
11681          */
11682         "update": true,
11683         /**
11684          * @event failure
11685          * Fired on update failure.
11686          * @param {Roo.Element} el
11687          * @param {Object} oResponseObject The response Object
11688          */
11689         "failure": true
11690     });
11691     var d = Roo.UpdateManager.defaults;
11692     /**
11693      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11694      * @type String
11695      */
11696     this.sslBlankUrl = d.sslBlankUrl;
11697     /**
11698      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11699      * @type Boolean
11700      */
11701     this.disableCaching = d.disableCaching;
11702     /**
11703      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11704      * @type String
11705      */
11706     this.indicatorText = d.indicatorText;
11707     /**
11708      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11709      * @type String
11710      */
11711     this.showLoadIndicator = d.showLoadIndicator;
11712     /**
11713      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11714      * @type Number
11715      */
11716     this.timeout = d.timeout;
11717
11718     /**
11719      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11720      * @type Boolean
11721      */
11722     this.loadScripts = d.loadScripts;
11723
11724     /**
11725      * Transaction object of current executing transaction
11726      */
11727     this.transaction = null;
11728
11729     /**
11730      * @private
11731      */
11732     this.autoRefreshProcId = null;
11733     /**
11734      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11735      * @type Function
11736      */
11737     this.refreshDelegate = this.refresh.createDelegate(this);
11738     /**
11739      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11740      * @type Function
11741      */
11742     this.updateDelegate = this.update.createDelegate(this);
11743     /**
11744      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11745      * @type Function
11746      */
11747     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11748     /**
11749      * @private
11750      */
11751     this.successDelegate = this.processSuccess.createDelegate(this);
11752     /**
11753      * @private
11754      */
11755     this.failureDelegate = this.processFailure.createDelegate(this);
11756
11757     if(!this.renderer){
11758      /**
11759       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11760       */
11761     this.renderer = new Roo.UpdateManager.BasicRenderer();
11762     }
11763     
11764     Roo.UpdateManager.superclass.constructor.call(this);
11765 };
11766
11767 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11768     /**
11769      * Get the Element this UpdateManager is bound to
11770      * @return {Roo.Element} The element
11771      */
11772     getEl : function(){
11773         return this.el;
11774     },
11775     /**
11776      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11777      * @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:
11778 <pre><code>
11779 um.update({<br/>
11780     url: "your-url.php",<br/>
11781     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11782     callback: yourFunction,<br/>
11783     scope: yourObject, //(optional scope)  <br/>
11784     discardUrl: false, <br/>
11785     nocache: false,<br/>
11786     text: "Loading...",<br/>
11787     timeout: 30,<br/>
11788     scripts: false<br/>
11789 });
11790 </code></pre>
11791      * The only required property is url. The optional properties nocache, text and scripts
11792      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11793      * @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}
11794      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11795      * @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.
11796      */
11797     update : function(url, params, callback, discardUrl){
11798         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11799             var method = this.method, cfg;
11800             if(typeof url == "object"){ // must be config object
11801                 cfg = url;
11802                 url = cfg.url;
11803                 params = params || cfg.params;
11804                 callback = callback || cfg.callback;
11805                 discardUrl = discardUrl || cfg.discardUrl;
11806                 if(callback && cfg.scope){
11807                     callback = callback.createDelegate(cfg.scope);
11808                 }
11809                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11810                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11811                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11812                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11813                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11814             }
11815             this.showLoading();
11816             if(!discardUrl){
11817                 this.defaultUrl = url;
11818             }
11819             if(typeof url == "function"){
11820                 url = url.call(this);
11821             }
11822
11823             method = method || (params ? "POST" : "GET");
11824             if(method == "GET"){
11825                 url = this.prepareUrl(url);
11826             }
11827
11828             var o = Roo.apply(cfg ||{}, {
11829                 url : url,
11830                 params: params,
11831                 success: this.successDelegate,
11832                 failure: this.failureDelegate,
11833                 callback: undefined,
11834                 timeout: (this.timeout*1000),
11835                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11836             });
11837
11838             this.transaction = Roo.Ajax.request(o);
11839         }
11840     },
11841
11842     /**
11843      * 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.
11844      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11845      * @param {String/HTMLElement} form The form Id or form element
11846      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11847      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11848      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11849      */
11850     formUpdate : function(form, url, reset, callback){
11851         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855             form = Roo.getDom(form);
11856             this.transaction = Roo.Ajax.request({
11857                 form: form,
11858                 url:url,
11859                 success: this.successDelegate,
11860                 failure: this.failureDelegate,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11863             });
11864             this.showLoading.defer(1, this);
11865         }
11866     },
11867
11868     /**
11869      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11870      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11871      */
11872     refresh : function(callback){
11873         if(this.defaultUrl == null){
11874             return;
11875         }
11876         this.update(this.defaultUrl, null, callback, true);
11877     },
11878
11879     /**
11880      * Set this element to auto refresh.
11881      * @param {Number} interval How often to update (in seconds).
11882      * @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)
11883      * @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}
11884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11885      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11886      */
11887     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11888         if(refreshNow){
11889             this.update(url || this.defaultUrl, params, callback, true);
11890         }
11891         if(this.autoRefreshProcId){
11892             clearInterval(this.autoRefreshProcId);
11893         }
11894         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11895     },
11896
11897     /**
11898      * Stop auto refresh on this element.
11899      */
11900      stopAutoRefresh : function(){
11901         if(this.autoRefreshProcId){
11902             clearInterval(this.autoRefreshProcId);
11903             delete this.autoRefreshProcId;
11904         }
11905     },
11906
11907     isAutoRefreshing : function(){
11908        return this.autoRefreshProcId ? true : false;
11909     },
11910     /**
11911      * Called to update the element to "Loading" state. Override to perform custom action.
11912      */
11913     showLoading : function(){
11914         if(this.showLoadIndicator){
11915             this.el.update(this.indicatorText);
11916         }
11917     },
11918
11919     /**
11920      * Adds unique parameter to query string if disableCaching = true
11921      * @private
11922      */
11923     prepareUrl : function(url){
11924         if(this.disableCaching){
11925             var append = "_dc=" + (new Date().getTime());
11926             if(url.indexOf("?") !== -1){
11927                 url += "&" + append;
11928             }else{
11929                 url += "?" + append;
11930             }
11931         }
11932         return url;
11933     },
11934
11935     /**
11936      * @private
11937      */
11938     processSuccess : function(response){
11939         this.transaction = null;
11940         if(response.argument.form && response.argument.reset){
11941             try{ // put in try/catch since some older FF releases had problems with this
11942                 response.argument.form.reset();
11943             }catch(e){}
11944         }
11945         if(this.loadScripts){
11946             this.renderer.render(this.el, response, this,
11947                 this.updateComplete.createDelegate(this, [response]));
11948         }else{
11949             this.renderer.render(this.el, response, this);
11950             this.updateComplete(response);
11951         }
11952     },
11953
11954     updateComplete : function(response){
11955         this.fireEvent("update", this.el, response);
11956         if(typeof response.argument.callback == "function"){
11957             response.argument.callback(this.el, true, response);
11958         }
11959     },
11960
11961     /**
11962      * @private
11963      */
11964     processFailure : function(response){
11965         this.transaction = null;
11966         this.fireEvent("failure", this.el, response);
11967         if(typeof response.argument.callback == "function"){
11968             response.argument.callback(this.el, false, response);
11969         }
11970     },
11971
11972     /**
11973      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11974      * @param {Object} renderer The object implementing the render() method
11975      */
11976     setRenderer : function(renderer){
11977         this.renderer = renderer;
11978     },
11979
11980     getRenderer : function(){
11981        return this.renderer;
11982     },
11983
11984     /**
11985      * Set the defaultUrl used for updates
11986      * @param {String/Function} defaultUrl The url or a function to call to get the url
11987      */
11988     setDefaultUrl : function(defaultUrl){
11989         this.defaultUrl = defaultUrl;
11990     },
11991
11992     /**
11993      * Aborts the executing transaction
11994      */
11995     abort : function(){
11996         if(this.transaction){
11997             Roo.Ajax.abort(this.transaction);
11998         }
11999     },
12000
12001     /**
12002      * Returns true if an update is in progress
12003      * @return {Boolean}
12004      */
12005     isUpdating : function(){
12006         if(this.transaction){
12007             return Roo.Ajax.isLoading(this.transaction);
12008         }
12009         return false;
12010     }
12011 });
12012
12013 /**
12014  * @class Roo.UpdateManager.defaults
12015  * @static (not really - but it helps the doc tool)
12016  * The defaults collection enables customizing the default properties of UpdateManager
12017  */
12018    Roo.UpdateManager.defaults = {
12019        /**
12020          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12021          * @type Number
12022          */
12023          timeout : 30,
12024
12025          /**
12026          * True to process scripts by default (Defaults to false).
12027          * @type Boolean
12028          */
12029         loadScripts : false,
12030
12031         /**
12032         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12033         * @type String
12034         */
12035         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12036         /**
12037          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12038          * @type Boolean
12039          */
12040         disableCaching : false,
12041         /**
12042          * Whether to show indicatorText when loading (Defaults to true).
12043          * @type Boolean
12044          */
12045         showLoadIndicator : true,
12046         /**
12047          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12048          * @type String
12049          */
12050         indicatorText : '<div class="loading-indicator">Loading...</div>'
12051    };
12052
12053 /**
12054  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12055  *Usage:
12056  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12057  * @param {String/HTMLElement/Roo.Element} el The element to update
12058  * @param {String} url The url
12059  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12060  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12061  * @static
12062  * @deprecated
12063  * @member Roo.UpdateManager
12064  */
12065 Roo.UpdateManager.updateElement = function(el, url, params, options){
12066     var um = Roo.get(el, true).getUpdateManager();
12067     Roo.apply(um, options);
12068     um.update(url, params, options ? options.callback : null);
12069 };
12070 // alias for backwards compat
12071 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12072 /**
12073  * @class Roo.UpdateManager.BasicRenderer
12074  * Default Content renderer. Updates the elements innerHTML with the responseText.
12075  */
12076 Roo.UpdateManager.BasicRenderer = function(){};
12077
12078 Roo.UpdateManager.BasicRenderer.prototype = {
12079     /**
12080      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12081      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12082      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12083      * @param {Roo.Element} el The element being rendered
12084      * @param {Object} response The YUI Connect response object
12085      * @param {UpdateManager} updateManager The calling update manager
12086      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12087      */
12088      render : function(el, response, updateManager, callback){
12089         el.update(response.responseText, updateManager.loadScripts, callback);
12090     }
12091 };
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104  * @class Roo.util.DelayedTask
12105  * Provides a convenient method of performing setTimeout where a new
12106  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12107  * You can use this class to buffer
12108  * the keypress events for a certain number of milliseconds, and perform only if they stop
12109  * for that amount of time.
12110  * @constructor The parameters to this constructor serve as defaults and are not required.
12111  * @param {Function} fn (optional) The default function to timeout
12112  * @param {Object} scope (optional) The default scope of that timeout
12113  * @param {Array} args (optional) The default Array of arguments
12114  */
12115 Roo.util.DelayedTask = function(fn, scope, args){
12116     var id = null, d, t;
12117
12118     var call = function(){
12119         var now = new Date().getTime();
12120         if(now - t >= d){
12121             clearInterval(id);
12122             id = null;
12123             fn.apply(scope, args || []);
12124         }
12125     };
12126     /**
12127      * Cancels any pending timeout and queues a new one
12128      * @param {Number} delay The milliseconds to delay
12129      * @param {Function} newFn (optional) Overrides function passed to constructor
12130      * @param {Object} newScope (optional) Overrides scope passed to constructor
12131      * @param {Array} newArgs (optional) Overrides args passed to constructor
12132      */
12133     this.delay = function(delay, newFn, newScope, newArgs){
12134         if(id && delay != d){
12135             this.cancel();
12136         }
12137         d = delay;
12138         t = new Date().getTime();
12139         fn = newFn || fn;
12140         scope = newScope || scope;
12141         args = newArgs || args;
12142         if(!id){
12143             id = setInterval(call, d);
12144         }
12145     };
12146
12147     /**
12148      * Cancel the last queued timeout
12149      */
12150     this.cancel = function(){
12151         if(id){
12152             clearInterval(id);
12153             id = null;
12154         }
12155     };
12156 };/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166  
12167  
12168 Roo.util.TaskRunner = function(interval){
12169     interval = interval || 10;
12170     var tasks = [], removeQueue = [];
12171     var id = 0;
12172     var running = false;
12173
12174     var stopThread = function(){
12175         running = false;
12176         clearInterval(id);
12177         id = 0;
12178     };
12179
12180     var startThread = function(){
12181         if(!running){
12182             running = true;
12183             id = setInterval(runTasks, interval);
12184         }
12185     };
12186
12187     var removeTask = function(task){
12188         removeQueue.push(task);
12189         if(task.onStop){
12190             task.onStop();
12191         }
12192     };
12193
12194     var runTasks = function(){
12195         if(removeQueue.length > 0){
12196             for(var i = 0, len = removeQueue.length; i < len; i++){
12197                 tasks.remove(removeQueue[i]);
12198             }
12199             removeQueue = [];
12200             if(tasks.length < 1){
12201                 stopThread();
12202                 return;
12203             }
12204         }
12205         var now = new Date().getTime();
12206         for(var i = 0, len = tasks.length; i < len; ++i){
12207             var t = tasks[i];
12208             var itime = now - t.taskRunTime;
12209             if(t.interval <= itime){
12210                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12211                 t.taskRunTime = now;
12212                 if(rt === false || t.taskRunCount === t.repeat){
12213                     removeTask(t);
12214                     return;
12215                 }
12216             }
12217             if(t.duration && t.duration <= (now - t.taskStartTime)){
12218                 removeTask(t);
12219             }
12220         }
12221     };
12222
12223     /**
12224      * Queues a new task.
12225      * @param {Object} task
12226      */
12227     this.start = function(task){
12228         tasks.push(task);
12229         task.taskStartTime = new Date().getTime();
12230         task.taskRunTime = 0;
12231         task.taskRunCount = 0;
12232         startThread();
12233         return task;
12234     };
12235
12236     this.stop = function(task){
12237         removeTask(task);
12238         return task;
12239     };
12240
12241     this.stopAll = function(){
12242         stopThread();
12243         for(var i = 0, len = tasks.length; i < len; i++){
12244             if(tasks[i].onStop){
12245                 tasks[i].onStop();
12246             }
12247         }
12248         tasks = [];
12249         removeQueue = [];
12250     };
12251 };
12252
12253 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263
12264  
12265 /**
12266  * @class Roo.util.MixedCollection
12267  * @extends Roo.util.Observable
12268  * A Collection class that maintains both numeric indexes and keys and exposes events.
12269  * @constructor
12270  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12271  * collection (defaults to false)
12272  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12273  * and return the key value for that item.  This is used when available to look up the key on items that
12274  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12275  * equivalent to providing an implementation for the {@link #getKey} method.
12276  */
12277 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12278     this.items = [];
12279     this.map = {};
12280     this.keys = [];
12281     this.length = 0;
12282     this.addEvents({
12283         /**
12284          * @event clear
12285          * Fires when the collection is cleared.
12286          */
12287         "clear" : true,
12288         /**
12289          * @event add
12290          * Fires when an item is added to the collection.
12291          * @param {Number} index The index at which the item was added.
12292          * @param {Object} o The item added.
12293          * @param {String} key The key associated with the added item.
12294          */
12295         "add" : true,
12296         /**
12297          * @event replace
12298          * Fires when an item is replaced in the collection.
12299          * @param {String} key he key associated with the new added.
12300          * @param {Object} old The item being replaced.
12301          * @param {Object} new The new item.
12302          */
12303         "replace" : true,
12304         /**
12305          * @event remove
12306          * Fires when an item is removed from the collection.
12307          * @param {Object} o The item being removed.
12308          * @param {String} key (optional) The key associated with the removed item.
12309          */
12310         "remove" : true,
12311         "sort" : true
12312     });
12313     this.allowFunctions = allowFunctions === true;
12314     if(keyFn){
12315         this.getKey = keyFn;
12316     }
12317     Roo.util.MixedCollection.superclass.constructor.call(this);
12318 };
12319
12320 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12321     allowFunctions : false,
12322     
12323 /**
12324  * Adds an item to the collection.
12325  * @param {String} key The key to associate with the item
12326  * @param {Object} o The item to add.
12327  * @return {Object} The item added.
12328  */
12329     add : function(key, o){
12330         if(arguments.length == 1){
12331             o = arguments[0];
12332             key = this.getKey(o);
12333         }
12334         if(typeof key == "undefined" || key === null){
12335             this.length++;
12336             this.items.push(o);
12337             this.keys.push(null);
12338         }else{
12339             var old = this.map[key];
12340             if(old){
12341                 return this.replace(key, o);
12342             }
12343             this.length++;
12344             this.items.push(o);
12345             this.map[key] = o;
12346             this.keys.push(key);
12347         }
12348         this.fireEvent("add", this.length-1, o, key);
12349         return o;
12350     },
12351        
12352 /**
12353   * MixedCollection has a generic way to fetch keys if you implement getKey.
12354 <pre><code>
12355 // normal way
12356 var mc = new Roo.util.MixedCollection();
12357 mc.add(someEl.dom.id, someEl);
12358 mc.add(otherEl.dom.id, otherEl);
12359 //and so on
12360
12361 // using getKey
12362 var mc = new Roo.util.MixedCollection();
12363 mc.getKey = function(el){
12364    return el.dom.id;
12365 };
12366 mc.add(someEl);
12367 mc.add(otherEl);
12368
12369 // or via the constructor
12370 var mc = new Roo.util.MixedCollection(false, function(el){
12371    return el.dom.id;
12372 });
12373 mc.add(someEl);
12374 mc.add(otherEl);
12375 </code></pre>
12376  * @param o {Object} The item for which to find the key.
12377  * @return {Object} The key for the passed item.
12378  */
12379     getKey : function(o){
12380          return o.id; 
12381     },
12382    
12383 /**
12384  * Replaces an item in the collection.
12385  * @param {String} key The key associated with the item to replace, or the item to replace.
12386  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12387  * @return {Object}  The new item.
12388  */
12389     replace : function(key, o){
12390         if(arguments.length == 1){
12391             o = arguments[0];
12392             key = this.getKey(o);
12393         }
12394         var old = this.item(key);
12395         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12396              return this.add(key, o);
12397         }
12398         var index = this.indexOfKey(key);
12399         this.items[index] = o;
12400         this.map[key] = o;
12401         this.fireEvent("replace", key, old, o);
12402         return o;
12403     },
12404    
12405 /**
12406  * Adds all elements of an Array or an Object to the collection.
12407  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12408  * an Array of values, each of which are added to the collection.
12409  */
12410     addAll : function(objs){
12411         if(arguments.length > 1 || objs instanceof Array){
12412             var args = arguments.length > 1 ? arguments : objs;
12413             for(var i = 0, len = args.length; i < len; i++){
12414                 this.add(args[i]);
12415             }
12416         }else{
12417             for(var key in objs){
12418                 if(this.allowFunctions || typeof objs[key] != "function"){
12419                     this.add(key, objs[key]);
12420                 }
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every item in the collection, passing each
12427  * item as the first and only parameter. returning false from the function will stop the iteration.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     each : function(fn, scope){
12432         var items = [].concat(this.items); // each safe for removal
12433         for(var i = 0, len = items.length; i < len; i++){
12434             if(fn.call(scope || items[i], items[i], i, len) === false){
12435                 break;
12436             }
12437         }
12438     },
12439    
12440 /**
12441  * Executes the specified function once for every key in the collection, passing each
12442  * key, and its associated item as the first two parameters.
12443  * @param {Function} fn The function to execute for each item.
12444  * @param {Object} scope (optional) The scope in which to execute the function.
12445  */
12446     eachKey : function(fn, scope){
12447         for(var i = 0, len = this.keys.length; i < len; i++){
12448             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12449         }
12450     },
12451    
12452 /**
12453  * Returns the first item in the collection which elicits a true return value from the
12454  * passed selection function.
12455  * @param {Function} fn The selection function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  * @return {Object} The first item in the collection which returned true from the selection function.
12458  */
12459     find : function(fn, scope){
12460         for(var i = 0, len = this.items.length; i < len; i++){
12461             if(fn.call(scope || window, this.items[i], this.keys[i])){
12462                 return this.items[i];
12463             }
12464         }
12465         return null;
12466     },
12467    
12468 /**
12469  * Inserts an item at the specified index in the collection.
12470  * @param {Number} index The index to insert the item at.
12471  * @param {String} key The key to associate with the new item, or the item itself.
12472  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12473  * @return {Object} The item inserted.
12474  */
12475     insert : function(index, key, o){
12476         if(arguments.length == 2){
12477             o = arguments[1];
12478             key = this.getKey(o);
12479         }
12480         if(index >= this.length){
12481             return this.add(key, o);
12482         }
12483         this.length++;
12484         this.items.splice(index, 0, o);
12485         if(typeof key != "undefined" && key != null){
12486             this.map[key] = o;
12487         }
12488         this.keys.splice(index, 0, key);
12489         this.fireEvent("add", index, o, key);
12490         return o;
12491     },
12492    
12493 /**
12494  * Removed an item from the collection.
12495  * @param {Object} o The item to remove.
12496  * @return {Object} The item removed.
12497  */
12498     remove : function(o){
12499         return this.removeAt(this.indexOf(o));
12500     },
12501    
12502 /**
12503  * Remove an item from a specified index in the collection.
12504  * @param {Number} index The index within the collection of the item to remove.
12505  */
12506     removeAt : function(index){
12507         if(index < this.length && index >= 0){
12508             this.length--;
12509             var o = this.items[index];
12510             this.items.splice(index, 1);
12511             var key = this.keys[index];
12512             if(typeof key != "undefined"){
12513                 delete this.map[key];
12514             }
12515             this.keys.splice(index, 1);
12516             this.fireEvent("remove", o, key);
12517         }
12518     },
12519    
12520 /**
12521  * Removed an item associated with the passed key fom the collection.
12522  * @param {String} key The key of the item to remove.
12523  */
12524     removeKey : function(key){
12525         return this.removeAt(this.indexOfKey(key));
12526     },
12527    
12528 /**
12529  * Returns the number of items in the collection.
12530  * @return {Number} the number of items in the collection.
12531  */
12532     getCount : function(){
12533         return this.length; 
12534     },
12535    
12536 /**
12537  * Returns index within the collection of the passed Object.
12538  * @param {Object} o The item to find the index of.
12539  * @return {Number} index of the item.
12540  */
12541     indexOf : function(o){
12542         if(!this.items.indexOf){
12543             for(var i = 0, len = this.items.length; i < len; i++){
12544                 if(this.items[i] == o) return i;
12545             }
12546             return -1;
12547         }else{
12548             return this.items.indexOf(o);
12549         }
12550     },
12551    
12552 /**
12553  * Returns index within the collection of the passed key.
12554  * @param {String} key The key to find the index of.
12555  * @return {Number} index of the key.
12556  */
12557     indexOfKey : function(key){
12558         if(!this.keys.indexOf){
12559             for(var i = 0, len = this.keys.length; i < len; i++){
12560                 if(this.keys[i] == key) return i;
12561             }
12562             return -1;
12563         }else{
12564             return this.keys.indexOf(key);
12565         }
12566     },
12567    
12568 /**
12569  * Returns the item associated with the passed key OR index. Key has priority over index.
12570  * @param {String/Number} key The key or index of the item.
12571  * @return {Object} The item associated with the passed key.
12572  */
12573     item : function(key){
12574         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12575         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12576     },
12577     
12578 /**
12579  * Returns the item at the specified index.
12580  * @param {Number} index The index of the item.
12581  * @return {Object}
12582  */
12583     itemAt : function(index){
12584         return this.items[index];
12585     },
12586     
12587 /**
12588  * Returns the item associated with the passed key.
12589  * @param {String/Number} key The key of the item.
12590  * @return {Object} The item associated with the passed key.
12591  */
12592     key : function(key){
12593         return this.map[key];
12594     },
12595    
12596 /**
12597  * Returns true if the collection contains the passed Object as an item.
12598  * @param {Object} o  The Object to look for in the collection.
12599  * @return {Boolean} True if the collection contains the Object as an item.
12600  */
12601     contains : function(o){
12602         return this.indexOf(o) != -1;
12603     },
12604    
12605 /**
12606  * Returns true if the collection contains the passed Object as a key.
12607  * @param {String} key The key to look for in the collection.
12608  * @return {Boolean} True if the collection contains the Object as a key.
12609  */
12610     containsKey : function(key){
12611         return typeof this.map[key] != "undefined";
12612     },
12613    
12614 /**
12615  * Removes all items from the collection.
12616  */
12617     clear : function(){
12618         this.length = 0;
12619         this.items = [];
12620         this.keys = [];
12621         this.map = {};
12622         this.fireEvent("clear");
12623     },
12624    
12625 /**
12626  * Returns the first item in the collection.
12627  * @return {Object} the first item in the collection..
12628  */
12629     first : function(){
12630         return this.items[0]; 
12631     },
12632    
12633 /**
12634  * Returns the last item in the collection.
12635  * @return {Object} the last item in the collection..
12636  */
12637     last : function(){
12638         return this.items[this.length-1];   
12639     },
12640     
12641     _sort : function(property, dir, fn){
12642         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12643         fn = fn || function(a, b){
12644             return a-b;
12645         };
12646         var c = [], k = this.keys, items = this.items;
12647         for(var i = 0, len = items.length; i < len; i++){
12648             c[c.length] = {key: k[i], value: items[i], index: i};
12649         }
12650         c.sort(function(a, b){
12651             var v = fn(a[property], b[property]) * dsc;
12652             if(v == 0){
12653                 v = (a.index < b.index ? -1 : 1);
12654             }
12655             return v;
12656         });
12657         for(var i = 0, len = c.length; i < len; i++){
12658             items[i] = c[i].value;
12659             k[i] = c[i].key;
12660         }
12661         this.fireEvent("sort", this);
12662     },
12663     
12664     /**
12665      * Sorts this collection with the passed comparison function
12666      * @param {String} direction (optional) "ASC" or "DESC"
12667      * @param {Function} fn (optional) comparison function
12668      */
12669     sort : function(dir, fn){
12670         this._sort("value", dir, fn);
12671     },
12672     
12673     /**
12674      * Sorts this collection by keys
12675      * @param {String} direction (optional) "ASC" or "DESC"
12676      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12677      */
12678     keySort : function(dir, fn){
12679         this._sort("key", dir, fn || function(a, b){
12680             return String(a).toUpperCase()-String(b).toUpperCase();
12681         });
12682     },
12683     
12684     /**
12685      * Returns a range of items in this collection
12686      * @param {Number} startIndex (optional) defaults to 0
12687      * @param {Number} endIndex (optional) default to the last item
12688      * @return {Array} An array of items
12689      */
12690     getRange : function(start, end){
12691         var items = this.items;
12692         if(items.length < 1){
12693             return [];
12694         }
12695         start = start || 0;
12696         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12697         var r = [];
12698         if(start <= end){
12699             for(var i = start; i <= end; i++) {
12700                     r[r.length] = items[i];
12701             }
12702         }else{
12703             for(var i = start; i >= end; i--) {
12704                     r[r.length] = items[i];
12705             }
12706         }
12707         return r;
12708     },
12709         
12710     /**
12711      * Filter the <i>objects</i> in this collection by a specific property. 
12712      * Returns a new collection that has been filtered.
12713      * @param {String} property A property on your objects
12714      * @param {String/RegExp} value Either string that the property values 
12715      * should start with or a RegExp to test against the property
12716      * @return {MixedCollection} The new filtered collection
12717      */
12718     filter : function(property, value){
12719         if(!value.exec){ // not a regex
12720             value = String(value);
12721             if(value.length == 0){
12722                 return this.clone();
12723             }
12724             value = new RegExp("^" + Roo.escapeRe(value), "i");
12725         }
12726         return this.filterBy(function(o){
12727             return o && value.test(o[property]);
12728         });
12729         },
12730     
12731     /**
12732      * Filter by a function. * Returns a new collection that has been filtered.
12733      * The passed function will be called with each 
12734      * object in the collection. If the function returns true, the value is included 
12735      * otherwise it is filtered.
12736      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12737      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12738      * @return {MixedCollection} The new filtered collection
12739      */
12740     filterBy : function(fn, scope){
12741         var r = new Roo.util.MixedCollection();
12742         r.getKey = this.getKey;
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             if(fn.call(scope||this, it[i], k[i])){
12746                                 r.add(k[i], it[i]);
12747                         }
12748         }
12749         return r;
12750     },
12751     
12752     /**
12753      * Creates a duplicate of this collection
12754      * @return {MixedCollection}
12755      */
12756     clone : function(){
12757         var r = new Roo.util.MixedCollection();
12758         var k = this.keys, it = this.items;
12759         for(var i = 0, len = it.length; i < len; i++){
12760             r.add(k[i], it[i]);
12761         }
12762         r.getKey = this.getKey;
12763         return r;
12764     }
12765 });
12766 /**
12767  * Returns the item associated with the passed key or index.
12768  * @method
12769  * @param {String/Number} key The key or index of the item.
12770  * @return {Object} The item associated with the passed key.
12771  */
12772 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782 /**
12783  * @class Roo.util.JSON
12784  * Modified version of Douglas Crockford"s json.js that doesn"t
12785  * mess with the Object prototype 
12786  * http://www.json.org/js.html
12787  * @singleton
12788  */
12789 Roo.util.JSON = new (function(){
12790     var useHasOwn = {}.hasOwnProperty ? true : false;
12791     
12792     // crashes Safari in some instances
12793     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12794     
12795     var pad = function(n) {
12796         return n < 10 ? "0" + n : n;
12797     };
12798     
12799     var m = {
12800         "\b": '\\b',
12801         "\t": '\\t',
12802         "\n": '\\n',
12803         "\f": '\\f',
12804         "\r": '\\r',
12805         '"' : '\\"',
12806         "\\": '\\\\'
12807     };
12808
12809     var encodeString = function(s){
12810         if (/["\\\x00-\x1f]/.test(s)) {
12811             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12812                 var c = m[b];
12813                 if(c){
12814                     return c;
12815                 }
12816                 c = b.charCodeAt();
12817                 return "\\u00" +
12818                     Math.floor(c / 16).toString(16) +
12819                     (c % 16).toString(16);
12820             }) + '"';
12821         }
12822         return '"' + s + '"';
12823     };
12824     
12825     var encodeArray = function(o){
12826         var a = ["["], b, i, l = o.length, v;
12827             for (i = 0; i < l; i += 1) {
12828                 v = o[i];
12829                 switch (typeof v) {
12830                     case "undefined":
12831                     case "function":
12832                     case "unknown":
12833                         break;
12834                     default:
12835                         if (b) {
12836                             a.push(',');
12837                         }
12838                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12839                         b = true;
12840                 }
12841             }
12842             a.push("]");
12843             return a.join("");
12844     };
12845     
12846     var encodeDate = function(o){
12847         return '"' + o.getFullYear() + "-" +
12848                 pad(o.getMonth() + 1) + "-" +
12849                 pad(o.getDate()) + "T" +
12850                 pad(o.getHours()) + ":" +
12851                 pad(o.getMinutes()) + ":" +
12852                 pad(o.getSeconds()) + '"';
12853     };
12854     
12855     /**
12856      * Encodes an Object, Array or other value
12857      * @param {Mixed} o The variable to encode
12858      * @return {String} The JSON string
12859      */
12860     this.encode = function(o)
12861     {
12862         // should this be extended to fully wrap stringify..
12863         
12864         if(typeof o == "undefined" || o === null){
12865             return "null";
12866         }else if(o instanceof Array){
12867             return encodeArray(o);
12868         }else if(o instanceof Date){
12869             return encodeDate(o);
12870         }else if(typeof o == "string"){
12871             return encodeString(o);
12872         }else if(typeof o == "number"){
12873             return isFinite(o) ? String(o) : "null";
12874         }else if(typeof o == "boolean"){
12875             return String(o);
12876         }else {
12877             var a = ["{"], b, i, v;
12878             for (i in o) {
12879                 if(!useHasOwn || o.hasOwnProperty(i)) {
12880                     v = o[i];
12881                     switch (typeof v) {
12882                     case "undefined":
12883                     case "function":
12884                     case "unknown":
12885                         break;
12886                     default:
12887                         if(b){
12888                             a.push(',');
12889                         }
12890                         a.push(this.encode(i), ":",
12891                                 v === null ? "null" : this.encode(v));
12892                         b = true;
12893                     }
12894                 }
12895             }
12896             a.push("}");
12897             return a.join("");
12898         }
12899     };
12900     
12901     /**
12902      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12903      * @param {String} json The JSON string
12904      * @return {Object} The resulting object
12905      */
12906     this.decode = function(json){
12907         
12908         return  /** eval:var:json */ eval("(" + json + ')');
12909     };
12910 })();
12911 /** 
12912  * Shorthand for {@link Roo.util.JSON#encode}
12913  * @member Roo encode 
12914  * @method */
12915 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12916 /** 
12917  * Shorthand for {@link Roo.util.JSON#decode}
12918  * @member Roo decode 
12919  * @method */
12920 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12921 /*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931  
12932 /**
12933  * @class Roo.util.Format
12934  * Reusable data formatting functions
12935  * @singleton
12936  */
12937 Roo.util.Format = function(){
12938     var trimRe = /^\s+|\s+$/g;
12939     return {
12940         /**
12941          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12942          * @param {String} value The string to truncate
12943          * @param {Number} length The maximum length to allow before truncating
12944          * @return {String} The converted text
12945          */
12946         ellipsis : function(value, len){
12947             if(value && value.length > len){
12948                 return value.substr(0, len-3)+"...";
12949             }
12950             return value;
12951         },
12952
12953         /**
12954          * Checks a reference and converts it to empty string if it is undefined
12955          * @param {Mixed} value Reference to check
12956          * @return {Mixed} Empty string if converted, otherwise the original value
12957          */
12958         undef : function(value){
12959             return typeof value != "undefined" ? value : "";
12960         },
12961
12962         /**
12963          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12964          * @param {String} value The string to encode
12965          * @return {String} The encoded text
12966          */
12967         htmlEncode : function(value){
12968             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12969         },
12970
12971         /**
12972          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12973          * @param {String} value The string to decode
12974          * @return {String} The decoded text
12975          */
12976         htmlDecode : function(value){
12977             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12978         },
12979
12980         /**
12981          * Trims any whitespace from either side of a string
12982          * @param {String} value The text to trim
12983          * @return {String} The trimmed text
12984          */
12985         trim : function(value){
12986             return String(value).replace(trimRe, "");
12987         },
12988
12989         /**
12990          * Returns a substring from within an original string
12991          * @param {String} value The original text
12992          * @param {Number} start The start index of the substring
12993          * @param {Number} length The length of the substring
12994          * @return {String} The substring
12995          */
12996         substr : function(value, start, length){
12997             return String(value).substr(start, length);
12998         },
12999
13000         /**
13001          * Converts a string to all lower case letters
13002          * @param {String} value The text to convert
13003          * @return {String} The converted text
13004          */
13005         lowercase : function(value){
13006             return String(value).toLowerCase();
13007         },
13008
13009         /**
13010          * Converts a string to all upper case letters
13011          * @param {String} value The text to convert
13012          * @return {String} The converted text
13013          */
13014         uppercase : function(value){
13015             return String(value).toUpperCase();
13016         },
13017
13018         /**
13019          * Converts the first character only of a string to upper case
13020          * @param {String} value The text to convert
13021          * @return {String} The converted text
13022          */
13023         capitalize : function(value){
13024             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13025         },
13026
13027         // private
13028         call : function(value, fn){
13029             if(arguments.length > 2){
13030                 var args = Array.prototype.slice.call(arguments, 2);
13031                 args.unshift(value);
13032                  
13033                 return /** eval:var:value */  eval(fn).apply(window, args);
13034             }else{
13035                 /** eval:var:value */
13036                 return /** eval:var:value */ eval(fn).call(window, value);
13037             }
13038         },
13039
13040        
13041         /**
13042          * safer version of Math.toFixed..??/
13043          * @param {Number/String} value The numeric value to format
13044          * @param {Number/String} value Decimal places 
13045          * @return {String} The formatted currency string
13046          */
13047         toFixed : function(v, n)
13048         {
13049             // why not use to fixed - precision is buggered???
13050             if (!n) {
13051                 return Math.round(v-0);
13052             }
13053             var fact = Math.pow(10,n+1);
13054             v = (Math.round((v-0)*fact))/fact;
13055             var z = (''+fact).substring(2);
13056             if (v == Math.floor(v)) {
13057                 return Math.floor(v) + '.' + z;
13058             }
13059             
13060             // now just padd decimals..
13061             var ps = String(v).split('.');
13062             var fd = (ps[1] + z);
13063             var r = fd.substring(0,n); 
13064             var rm = fd.substring(n); 
13065             if (rm < 5) {
13066                 return ps[0] + '.' + r;
13067             }
13068             r*=1; // turn it into a number;
13069             r++;
13070             if (String(r).length != n) {
13071                 ps[0]*=1;
13072                 ps[0]++;
13073                 r = String(r).substring(1); // chop the end off.
13074             }
13075             
13076             return ps[0] + '.' + r;
13077              
13078         },
13079         
13080         /**
13081          * Format a number as US currency
13082          * @param {Number/String} value The numeric value to format
13083          * @return {String} The formatted currency string
13084          */
13085         usMoney : function(v){
13086             v = (Math.round((v-0)*100))/100;
13087             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13088             v = String(v);
13089             var ps = v.split('.');
13090             var whole = ps[0];
13091             var sub = ps[1] ? '.'+ ps[1] : '.00';
13092             var r = /(\d+)(\d{3})/;
13093             while (r.test(whole)) {
13094                 whole = whole.replace(r, '$1' + ',' + '$2');
13095             }
13096             return "$" + whole + sub ;
13097         },
13098         
13099         /**
13100          * Parse a value into a formatted date using the specified format pattern.
13101          * @param {Mixed} value The value to format
13102          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13103          * @return {String} The formatted date string
13104          */
13105         date : function(v, format){
13106             if(!v){
13107                 return "";
13108             }
13109             if(!(v instanceof Date)){
13110                 v = new Date(Date.parse(v));
13111             }
13112             return v.dateFormat(format || "m/d/Y");
13113         },
13114
13115         /**
13116          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13117          * @param {String} format Any valid date format string
13118          * @return {Function} The date formatting function
13119          */
13120         dateRenderer : function(format){
13121             return function(v){
13122                 return Roo.util.Format.date(v, format);  
13123             };
13124         },
13125
13126         // private
13127         stripTagsRE : /<\/?[^>]+>/gi,
13128         
13129         /**
13130          * Strips all HTML tags
13131          * @param {Mixed} value The text from which to strip tags
13132          * @return {String} The stripped text
13133          */
13134         stripTags : function(v){
13135             return !v ? v : String(v).replace(this.stripTagsRE, "");
13136         }
13137     };
13138 }();/*
13139  * Based on:
13140  * Ext JS Library 1.1.1
13141  * Copyright(c) 2006-2007, Ext JS, LLC.
13142  *
13143  * Originally Released Under LGPL - original licence link has changed is not relivant.
13144  *
13145  * Fork - LGPL
13146  * <script type="text/javascript">
13147  */
13148
13149
13150  
13151
13152 /**
13153  * @class Roo.MasterTemplate
13154  * @extends Roo.Template
13155  * Provides a template that can have child templates. The syntax is:
13156 <pre><code>
13157 var t = new Roo.MasterTemplate(
13158         '&lt;select name="{name}"&gt;',
13159                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13160         '&lt;/select&gt;'
13161 );
13162 t.add('options', {value: 'foo', text: 'bar'});
13163 // or you can add multiple child elements in one shot
13164 t.addAll('options', [
13165     {value: 'foo', text: 'bar'},
13166     {value: 'foo2', text: 'bar2'},
13167     {value: 'foo3', text: 'bar3'}
13168 ]);
13169 // then append, applying the master template values
13170 t.append('my-form', {name: 'my-select'});
13171 </code></pre>
13172 * A name attribute for the child template is not required if you have only one child
13173 * template or you want to refer to them by index.
13174  */
13175 Roo.MasterTemplate = function(){
13176     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13177     this.originalHtml = this.html;
13178     var st = {};
13179     var m, re = this.subTemplateRe;
13180     re.lastIndex = 0;
13181     var subIndex = 0;
13182     while(m = re.exec(this.html)){
13183         var name = m[1], content = m[2];
13184         st[subIndex] = {
13185             name: name,
13186             index: subIndex,
13187             buffer: [],
13188             tpl : new Roo.Template(content)
13189         };
13190         if(name){
13191             st[name] = st[subIndex];
13192         }
13193         st[subIndex].tpl.compile();
13194         st[subIndex].tpl.call = this.call.createDelegate(this);
13195         subIndex++;
13196     }
13197     this.subCount = subIndex;
13198     this.subs = st;
13199 };
13200 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13201     /**
13202     * The regular expression used to match sub templates
13203     * @type RegExp
13204     * @property
13205     */
13206     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13207
13208     /**
13209      * Applies the passed values to a child template.
13210      * @param {String/Number} name (optional) The name or index of the child template
13211      * @param {Array/Object} values The values to be applied to the template
13212      * @return {MasterTemplate} this
13213      */
13214      add : function(name, values){
13215         if(arguments.length == 1){
13216             values = arguments[0];
13217             name = 0;
13218         }
13219         var s = this.subs[name];
13220         s.buffer[s.buffer.length] = s.tpl.apply(values);
13221         return this;
13222     },
13223
13224     /**
13225      * Applies all the passed values to a child template.
13226      * @param {String/Number} name (optional) The name or index of the child template
13227      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13228      * @param {Boolean} reset (optional) True to reset the template first
13229      * @return {MasterTemplate} this
13230      */
13231     fill : function(name, values, reset){
13232         var a = arguments;
13233         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13234             values = a[0];
13235             name = 0;
13236             reset = a[1];
13237         }
13238         if(reset){
13239             this.reset();
13240         }
13241         for(var i = 0, len = values.length; i < len; i++){
13242             this.add(name, values[i]);
13243         }
13244         return this;
13245     },
13246
13247     /**
13248      * Resets the template for reuse
13249      * @return {MasterTemplate} this
13250      */
13251      reset : function(){
13252         var s = this.subs;
13253         for(var i = 0; i < this.subCount; i++){
13254             s[i].buffer = [];
13255         }
13256         return this;
13257     },
13258
13259     applyTemplate : function(values){
13260         var s = this.subs;
13261         var replaceIndex = -1;
13262         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13263             return s[++replaceIndex].buffer.join("");
13264         });
13265         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13266     },
13267
13268     apply : function(){
13269         return this.applyTemplate.apply(this, arguments);
13270     },
13271
13272     compile : function(){return this;}
13273 });
13274
13275 /**
13276  * Alias for fill().
13277  * @method
13278  */
13279 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13280  /**
13281  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13282  * var tpl = Roo.MasterTemplate.from('element-id');
13283  * @param {String/HTMLElement} el
13284  * @param {Object} config
13285  * @static
13286  */
13287 Roo.MasterTemplate.from = function(el, config){
13288     el = Roo.getDom(el);
13289     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13290 };/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300
13301  
13302 /**
13303  * @class Roo.util.CSS
13304  * Utility class for manipulating CSS rules
13305  * @singleton
13306  */
13307 Roo.util.CSS = function(){
13308         var rules = null;
13309         var doc = document;
13310
13311     var camelRe = /(-[a-z])/gi;
13312     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13313
13314    return {
13315    /**
13316     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13317     * tag and appended to the HEAD of the document.
13318     * @param {String|Object} cssText The text containing the css rules
13319     * @param {String} id An id to add to the stylesheet for later removal
13320     * @return {StyleSheet}
13321     */
13322     createStyleSheet : function(cssText, id){
13323         var ss;
13324         var head = doc.getElementsByTagName("head")[0];
13325         var nrules = doc.createElement("style");
13326         nrules.setAttribute("type", "text/css");
13327         if(id){
13328             nrules.setAttribute("id", id);
13329         }
13330         if (typeof(cssText) != 'string') {
13331             // support object maps..
13332             // not sure if this a good idea.. 
13333             // perhaps it should be merged with the general css handling
13334             // and handle js style props.
13335             var cssTextNew = [];
13336             for(var n in cssText) {
13337                 var citems = [];
13338                 for(var k in cssText[n]) {
13339                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13340                 }
13341                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13342                 
13343             }
13344             cssText = cssTextNew.join("\n");
13345             
13346         }
13347        
13348        
13349        if(Roo.isIE){
13350            head.appendChild(nrules);
13351            ss = nrules.styleSheet;
13352            ss.cssText = cssText;
13353        }else{
13354            try{
13355                 nrules.appendChild(doc.createTextNode(cssText));
13356            }catch(e){
13357                nrules.cssText = cssText; 
13358            }
13359            head.appendChild(nrules);
13360            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13361        }
13362        this.cacheStyleSheet(ss);
13363        return ss;
13364    },
13365
13366    /**
13367     * Removes a style or link tag by id
13368     * @param {String} id The id of the tag
13369     */
13370    removeStyleSheet : function(id){
13371        var existing = doc.getElementById(id);
13372        if(existing){
13373            existing.parentNode.removeChild(existing);
13374        }
13375    },
13376
13377    /**
13378     * Dynamically swaps an existing stylesheet reference for a new one
13379     * @param {String} id The id of an existing link tag to remove
13380     * @param {String} url The href of the new stylesheet to include
13381     */
13382    swapStyleSheet : function(id, url){
13383        this.removeStyleSheet(id);
13384        var ss = doc.createElement("link");
13385        ss.setAttribute("rel", "stylesheet");
13386        ss.setAttribute("type", "text/css");
13387        ss.setAttribute("id", id);
13388        ss.setAttribute("href", url);
13389        doc.getElementsByTagName("head")[0].appendChild(ss);
13390    },
13391    
13392    /**
13393     * Refresh the rule cache if you have dynamically added stylesheets
13394     * @return {Object} An object (hash) of rules indexed by selector
13395     */
13396    refreshCache : function(){
13397        return this.getRules(true);
13398    },
13399
13400    // private
13401    cacheStyleSheet : function(stylesheet){
13402        if(!rules){
13403            rules = {};
13404        }
13405        try{// try catch for cross domain access issue
13406            var ssRules = stylesheet.cssRules || stylesheet.rules;
13407            for(var j = ssRules.length-1; j >= 0; --j){
13408                rules[ssRules[j].selectorText] = ssRules[j];
13409            }
13410        }catch(e){}
13411    },
13412    
13413    /**
13414     * Gets all css rules for the document
13415     * @param {Boolean} refreshCache true to refresh the internal cache
13416     * @return {Object} An object (hash) of rules indexed by selector
13417     */
13418    getRules : function(refreshCache){
13419                 if(rules == null || refreshCache){
13420                         rules = {};
13421                         var ds = doc.styleSheets;
13422                         for(var i =0, len = ds.length; i < len; i++){
13423                             try{
13424                         this.cacheStyleSheet(ds[i]);
13425                     }catch(e){} 
13426                 }
13427                 }
13428                 return rules;
13429         },
13430         
13431         /**
13432     * Gets an an individual CSS rule by selector(s)
13433     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13434     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13435     * @return {CSSRule} The CSS rule or null if one is not found
13436     */
13437    getRule : function(selector, refreshCache){
13438                 var rs = this.getRules(refreshCache);
13439                 if(!(selector instanceof Array)){
13440                     return rs[selector];
13441                 }
13442                 for(var i = 0; i < selector.length; i++){
13443                         if(rs[selector[i]]){
13444                                 return rs[selector[i]];
13445                         }
13446                 }
13447                 return null;
13448         },
13449         
13450         
13451         /**
13452     * Updates a rule property
13453     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13454     * @param {String} property The css property
13455     * @param {String} value The new value for the property
13456     * @return {Boolean} true If a rule was found and updated
13457     */
13458    updateRule : function(selector, property, value){
13459                 if(!(selector instanceof Array)){
13460                         var rule = this.getRule(selector);
13461                         if(rule){
13462                                 rule.style[property.replace(camelRe, camelFn)] = value;
13463                                 return true;
13464                         }
13465                 }else{
13466                         for(var i = 0; i < selector.length; i++){
13467                                 if(this.updateRule(selector[i], property, value)){
13468                                         return true;
13469                                 }
13470                         }
13471                 }
13472                 return false;
13473         }
13474    };   
13475 }();/*
13476  * Based on:
13477  * Ext JS Library 1.1.1
13478  * Copyright(c) 2006-2007, Ext JS, LLC.
13479  *
13480  * Originally Released Under LGPL - original licence link has changed is not relivant.
13481  *
13482  * Fork - LGPL
13483  * <script type="text/javascript">
13484  */
13485
13486  
13487
13488 /**
13489  * @class Roo.util.ClickRepeater
13490  * @extends Roo.util.Observable
13491  * 
13492  * A wrapper class which can be applied to any element. Fires a "click" event while the
13493  * mouse is pressed. The interval between firings may be specified in the config but
13494  * defaults to 10 milliseconds.
13495  * 
13496  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13497  * 
13498  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13499  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13500  * Similar to an autorepeat key delay.
13501  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13502  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13503  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13504  *           "interval" and "delay" are ignored. "immediate" is honored.
13505  * @cfg {Boolean} preventDefault True to prevent the default click event
13506  * @cfg {Boolean} stopDefault True to stop the default click event
13507  * 
13508  * @history
13509  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13510  *     2007-02-02 jvs Renamed to ClickRepeater
13511  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13512  *
13513  *  @constructor
13514  * @param {String/HTMLElement/Element} el The element to listen on
13515  * @param {Object} config
13516  **/
13517 Roo.util.ClickRepeater = function(el, config)
13518 {
13519     this.el = Roo.get(el);
13520     this.el.unselectable();
13521
13522     Roo.apply(this, config);
13523
13524     this.addEvents({
13525     /**
13526      * @event mousedown
13527      * Fires when the mouse button is depressed.
13528      * @param {Roo.util.ClickRepeater} this
13529      */
13530         "mousedown" : true,
13531     /**
13532      * @event click
13533      * Fires on a specified interval during the time the element is pressed.
13534      * @param {Roo.util.ClickRepeater} this
13535      */
13536         "click" : true,
13537     /**
13538      * @event mouseup
13539      * Fires when the mouse key is released.
13540      * @param {Roo.util.ClickRepeater} this
13541      */
13542         "mouseup" : true
13543     });
13544
13545     this.el.on("mousedown", this.handleMouseDown, this);
13546     if(this.preventDefault || this.stopDefault){
13547         this.el.on("click", function(e){
13548             if(this.preventDefault){
13549                 e.preventDefault();
13550             }
13551             if(this.stopDefault){
13552                 e.stopEvent();
13553             }
13554         }, this);
13555     }
13556
13557     // allow inline handler
13558     if(this.handler){
13559         this.on("click", this.handler,  this.scope || this);
13560     }
13561
13562     Roo.util.ClickRepeater.superclass.constructor.call(this);
13563 };
13564
13565 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13566     interval : 20,
13567     delay: 250,
13568     preventDefault : true,
13569     stopDefault : false,
13570     timer : 0,
13571
13572     // private
13573     handleMouseDown : function(){
13574         clearTimeout(this.timer);
13575         this.el.blur();
13576         if(this.pressClass){
13577             this.el.addClass(this.pressClass);
13578         }
13579         this.mousedownTime = new Date();
13580
13581         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13582         this.el.on("mouseout", this.handleMouseOut, this);
13583
13584         this.fireEvent("mousedown", this);
13585         this.fireEvent("click", this);
13586         
13587         this.timer = this.click.defer(this.delay || this.interval, this);
13588     },
13589
13590     // private
13591     click : function(){
13592         this.fireEvent("click", this);
13593         this.timer = this.click.defer(this.getInterval(), this);
13594     },
13595
13596     // private
13597     getInterval: function(){
13598         if(!this.accelerate){
13599             return this.interval;
13600         }
13601         var pressTime = this.mousedownTime.getElapsed();
13602         if(pressTime < 500){
13603             return 400;
13604         }else if(pressTime < 1700){
13605             return 320;
13606         }else if(pressTime < 2600){
13607             return 250;
13608         }else if(pressTime < 3500){
13609             return 180;
13610         }else if(pressTime < 4400){
13611             return 140;
13612         }else if(pressTime < 5300){
13613             return 80;
13614         }else if(pressTime < 6200){
13615             return 50;
13616         }else{
13617             return 10;
13618         }
13619     },
13620
13621     // private
13622     handleMouseOut : function(){
13623         clearTimeout(this.timer);
13624         if(this.pressClass){
13625             this.el.removeClass(this.pressClass);
13626         }
13627         this.el.on("mouseover", this.handleMouseReturn, this);
13628     },
13629
13630     // private
13631     handleMouseReturn : function(){
13632         this.el.un("mouseover", this.handleMouseReturn);
13633         if(this.pressClass){
13634             this.el.addClass(this.pressClass);
13635         }
13636         this.click();
13637     },
13638
13639     // private
13640     handleMouseUp : function(){
13641         clearTimeout(this.timer);
13642         this.el.un("mouseover", this.handleMouseReturn);
13643         this.el.un("mouseout", this.handleMouseOut);
13644         Roo.get(document).un("mouseup", this.handleMouseUp);
13645         this.el.removeClass(this.pressClass);
13646         this.fireEvent("mouseup", this);
13647     }
13648 });/*
13649  * Based on:
13650  * Ext JS Library 1.1.1
13651  * Copyright(c) 2006-2007, Ext JS, LLC.
13652  *
13653  * Originally Released Under LGPL - original licence link has changed is not relivant.
13654  *
13655  * Fork - LGPL
13656  * <script type="text/javascript">
13657  */
13658
13659  
13660 /**
13661  * @class Roo.KeyNav
13662  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13663  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13664  * way to implement custom navigation schemes for any UI component.</p>
13665  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13666  * pageUp, pageDown, del, home, end.  Usage:</p>
13667  <pre><code>
13668 var nav = new Roo.KeyNav("my-element", {
13669     "left" : function(e){
13670         this.moveLeft(e.ctrlKey);
13671     },
13672     "right" : function(e){
13673         this.moveRight(e.ctrlKey);
13674     },
13675     "enter" : function(e){
13676         this.save();
13677     },
13678     scope : this
13679 });
13680 </code></pre>
13681  * @constructor
13682  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13683  * @param {Object} config The config
13684  */
13685 Roo.KeyNav = function(el, config){
13686     this.el = Roo.get(el);
13687     Roo.apply(this, config);
13688     if(!this.disabled){
13689         this.disabled = true;
13690         this.enable();
13691     }
13692 };
13693
13694 Roo.KeyNav.prototype = {
13695     /**
13696      * @cfg {Boolean} disabled
13697      * True to disable this KeyNav instance (defaults to false)
13698      */
13699     disabled : false,
13700     /**
13701      * @cfg {String} defaultEventAction
13702      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13703      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13704      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13705      */
13706     defaultEventAction: "stopEvent",
13707     /**
13708      * @cfg {Boolean} forceKeyDown
13709      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13710      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13711      * handle keydown instead of keypress.
13712      */
13713     forceKeyDown : false,
13714
13715     // private
13716     prepareEvent : function(e){
13717         var k = e.getKey();
13718         var h = this.keyToHandler[k];
13719         //if(h && this[h]){
13720         //    e.stopPropagation();
13721         //}
13722         if(Roo.isSafari && h && k >= 37 && k <= 40){
13723             e.stopEvent();
13724         }
13725     },
13726
13727     // private
13728     relay : function(e){
13729         var k = e.getKey();
13730         var h = this.keyToHandler[k];
13731         if(h && this[h]){
13732             if(this.doRelay(e, this[h], h) !== true){
13733                 e[this.defaultEventAction]();
13734             }
13735         }
13736     },
13737
13738     // private
13739     doRelay : function(e, h, hname){
13740         return h.call(this.scope || this, e);
13741     },
13742
13743     // possible handlers
13744     enter : false,
13745     left : false,
13746     right : false,
13747     up : false,
13748     down : false,
13749     tab : false,
13750     esc : false,
13751     pageUp : false,
13752     pageDown : false,
13753     del : false,
13754     home : false,
13755     end : false,
13756
13757     // quick lookup hash
13758     keyToHandler : {
13759         37 : "left",
13760         39 : "right",
13761         38 : "up",
13762         40 : "down",
13763         33 : "pageUp",
13764         34 : "pageDown",
13765         46 : "del",
13766         36 : "home",
13767         35 : "end",
13768         13 : "enter",
13769         27 : "esc",
13770         9  : "tab"
13771     },
13772
13773         /**
13774          * Enable this KeyNav
13775          */
13776         enable: function(){
13777                 if(this.disabled){
13778             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13779             // the EventObject will normalize Safari automatically
13780             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.on("keydown", this.relay,  this);
13782             }else{
13783                 this.el.on("keydown", this.prepareEvent,  this);
13784                 this.el.on("keypress", this.relay,  this);
13785             }
13786                     this.disabled = false;
13787                 }
13788         },
13789
13790         /**
13791          * Disable this KeyNav
13792          */
13793         disable: function(){
13794                 if(!this.disabled){
13795                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13796                 this.el.un("keydown", this.relay);
13797             }else{
13798                 this.el.un("keydown", this.prepareEvent);
13799                 this.el.un("keypress", this.relay);
13800             }
13801                     this.disabled = true;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.KeyMap
13818  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13819  * The constructor accepts the same config object as defined by {@link #addBinding}.
13820  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13821  * combination it will call the function with this signature (if the match is a multi-key
13822  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13823  * A KeyMap can also handle a string representation of keys.<br />
13824  * Usage:
13825  <pre><code>
13826 // map one key by key code
13827 var map = new Roo.KeyMap("my-element", {
13828     key: 13, // or Roo.EventObject.ENTER
13829     fn: myHandler,
13830     scope: myObject
13831 });
13832
13833 // map multiple keys to one action by string
13834 var map = new Roo.KeyMap("my-element", {
13835     key: "a\r\n\t",
13836     fn: myHandler,
13837     scope: myObject
13838 });
13839
13840 // map multiple keys to multiple actions by strings and array of codes
13841 var map = new Roo.KeyMap("my-element", [
13842     {
13843         key: [10,13],
13844         fn: function(){ alert("Return was pressed"); }
13845     }, {
13846         key: "abc",
13847         fn: function(){ alert('a, b or c was pressed'); }
13848     }, {
13849         key: "\t",
13850         ctrl:true,
13851         shift:true,
13852         fn: function(){ alert('Control + shift + tab was pressed.'); }
13853     }
13854 ]);
13855 </code></pre>
13856  * <b>Note: A KeyMap starts enabled</b>
13857  * @constructor
13858  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13859  * @param {Object} config The config (see {@link #addBinding})
13860  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13861  */
13862 Roo.KeyMap = function(el, config, eventName){
13863     this.el  = Roo.get(el);
13864     this.eventName = eventName || "keydown";
13865     this.bindings = [];
13866     if(config){
13867         this.addBinding(config);
13868     }
13869     this.enable();
13870 };
13871
13872 Roo.KeyMap.prototype = {
13873     /**
13874      * True to stop the event from bubbling and prevent the default browser action if the
13875      * key was handled by the KeyMap (defaults to false)
13876      * @type Boolean
13877      */
13878     stopEvent : false,
13879
13880     /**
13881      * Add a new binding to this KeyMap. The following config object properties are supported:
13882      * <pre>
13883 Property    Type             Description
13884 ----------  ---------------  ----------------------------------------------------------------------
13885 key         String/Array     A single keycode or an array of keycodes to handle
13886 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13887 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13888 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13889 fn          Function         The function to call when KeyMap finds the expected key combination
13890 scope       Object           The scope of the callback function
13891 </pre>
13892      *
13893      * Usage:
13894      * <pre><code>
13895 // Create a KeyMap
13896 var map = new Roo.KeyMap(document, {
13897     key: Roo.EventObject.ENTER,
13898     fn: handleKey,
13899     scope: this
13900 });
13901
13902 //Add a new binding to the existing KeyMap later
13903 map.addBinding({
13904     key: 'abc',
13905     shift: true,
13906     fn: handleKey,
13907     scope: this
13908 });
13909 </code></pre>
13910      * @param {Object/Array} config A single KeyMap config or an array of configs
13911      */
13912         addBinding : function(config){
13913         if(config instanceof Array){
13914             for(var i = 0, len = config.length; i < len; i++){
13915                 this.addBinding(config[i]);
13916             }
13917             return;
13918         }
13919         var keyCode = config.key,
13920             shift = config.shift, 
13921             ctrl = config.ctrl, 
13922             alt = config.alt,
13923             fn = config.fn,
13924             scope = config.scope;
13925         if(typeof keyCode == "string"){
13926             var ks = [];
13927             var keyString = keyCode.toUpperCase();
13928             for(var j = 0, len = keyString.length; j < len; j++){
13929                 ks.push(keyString.charCodeAt(j));
13930             }
13931             keyCode = ks;
13932         }
13933         var keyArray = keyCode instanceof Array;
13934         var handler = function(e){
13935             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13936                 var k = e.getKey();
13937                 if(keyArray){
13938                     for(var i = 0, len = keyCode.length; i < len; i++){
13939                         if(keyCode[i] == k){
13940                           if(this.stopEvent){
13941                               e.stopEvent();
13942                           }
13943                           fn.call(scope || window, k, e);
13944                           return;
13945                         }
13946                     }
13947                 }else{
13948                     if(k == keyCode){
13949                         if(this.stopEvent){
13950                            e.stopEvent();
13951                         }
13952                         fn.call(scope || window, k, e);
13953                     }
13954                 }
13955             }
13956         };
13957         this.bindings.push(handler);  
13958         },
13959
13960     /**
13961      * Shorthand for adding a single key listener
13962      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13963      * following options:
13964      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13965      * @param {Function} fn The function to call
13966      * @param {Object} scope (optional) The scope of the function
13967      */
13968     on : function(key, fn, scope){
13969         var keyCode, shift, ctrl, alt;
13970         if(typeof key == "object" && !(key instanceof Array)){
13971             keyCode = key.key;
13972             shift = key.shift;
13973             ctrl = key.ctrl;
13974             alt = key.alt;
13975         }else{
13976             keyCode = key;
13977         }
13978         this.addBinding({
13979             key: keyCode,
13980             shift: shift,
13981             ctrl: ctrl,
13982             alt: alt,
13983             fn: fn,
13984             scope: scope
13985         })
13986     },
13987
13988     // private
13989     handleKeyDown : function(e){
13990             if(this.enabled){ //just in case
13991             var b = this.bindings;
13992             for(var i = 0, len = b.length; i < len; i++){
13993                 b[i].call(this, e);
13994             }
13995             }
13996         },
13997         
13998         /**
13999          * Returns true if this KeyMap is enabled
14000          * @return {Boolean} 
14001          */
14002         isEnabled : function(){
14003             return this.enabled;  
14004         },
14005         
14006         /**
14007          * Enables this KeyMap
14008          */
14009         enable: function(){
14010                 if(!this.enabled){
14011                     this.el.on(this.eventName, this.handleKeyDown, this);
14012                     this.enabled = true;
14013                 }
14014         },
14015
14016         /**
14017          * Disable this KeyMap
14018          */
14019         disable: function(){
14020                 if(this.enabled){
14021                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14022                     this.enabled = false;
14023                 }
14024         }
14025 };/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037 /**
14038  * @class Roo.util.TextMetrics
14039  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14040  * wide, in pixels, a given block of text will be.
14041  * @singleton
14042  */
14043 Roo.util.TextMetrics = function(){
14044     var shared;
14045     return {
14046         /**
14047          * Measures the size of the specified text
14048          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14049          * that can affect the size of the rendered text
14050          * @param {String} text The text to measure
14051          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14052          * in order to accurately measure the text height
14053          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14054          */
14055         measure : function(el, text, fixedWidth){
14056             if(!shared){
14057                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14058             }
14059             shared.bind(el);
14060             shared.setFixedWidth(fixedWidth || 'auto');
14061             return shared.getSize(text);
14062         },
14063
14064         /**
14065          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14066          * the overhead of multiple calls to initialize the style properties on each measurement.
14067          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14068          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14069          * in order to accurately measure the text height
14070          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14071          */
14072         createInstance : function(el, fixedWidth){
14073             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14074         }
14075     };
14076 }();
14077
14078  
14079
14080 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14081     var ml = new Roo.Element(document.createElement('div'));
14082     document.body.appendChild(ml.dom);
14083     ml.position('absolute');
14084     ml.setLeftTop(-1000, -1000);
14085     ml.hide();
14086
14087     if(fixedWidth){
14088         ml.setWidth(fixedWidth);
14089     }
14090      
14091     var instance = {
14092         /**
14093          * Returns the size of the specified text based on the internal element's style and width properties
14094          * @memberOf Roo.util.TextMetrics.Instance#
14095          * @param {String} text The text to measure
14096          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14097          */
14098         getSize : function(text){
14099             ml.update(text);
14100             var s = ml.getSize();
14101             ml.update('');
14102             return s;
14103         },
14104
14105         /**
14106          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14107          * that can affect the size of the rendered text
14108          * @memberOf Roo.util.TextMetrics.Instance#
14109          * @param {String/HTMLElement} el The element, dom node or id
14110          */
14111         bind : function(el){
14112             ml.setStyle(
14113                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14114             );
14115         },
14116
14117         /**
14118          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14119          * to set a fixed width in order to accurately measure the text height.
14120          * @memberOf Roo.util.TextMetrics.Instance#
14121          * @param {Number} width The width to set on the element
14122          */
14123         setFixedWidth : function(width){
14124             ml.setWidth(width);
14125         },
14126
14127         /**
14128          * Returns the measured width of the specified text
14129          * @memberOf Roo.util.TextMetrics.Instance#
14130          * @param {String} text The text to measure
14131          * @return {Number} width The width in pixels
14132          */
14133         getWidth : function(text){
14134             ml.dom.style.width = 'auto';
14135             return this.getSize(text).width;
14136         },
14137
14138         /**
14139          * Returns the measured height of the specified text.  For multiline text, be sure to call
14140          * {@link #setFixedWidth} if necessary.
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String} text The text to measure
14143          * @return {Number} height The height in pixels
14144          */
14145         getHeight : function(text){
14146             return this.getSize(text).height;
14147         }
14148     };
14149
14150     instance.bind(bindTo);
14151
14152     return instance;
14153 };
14154
14155 // backwards compat
14156 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14157  * Based on:
14158  * Ext JS Library 1.1.1
14159  * Copyright(c) 2006-2007, Ext JS, LLC.
14160  *
14161  * Originally Released Under LGPL - original licence link has changed is not relivant.
14162  *
14163  * Fork - LGPL
14164  * <script type="text/javascript">
14165  */
14166
14167 /**
14168  * @class Roo.state.Provider
14169  * Abstract base class for state provider implementations. This class provides methods
14170  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14171  * Provider interface.
14172  */
14173 Roo.state.Provider = function(){
14174     /**
14175      * @event statechange
14176      * Fires when a state change occurs.
14177      * @param {Provider} this This state provider
14178      * @param {String} key The state key which was changed
14179      * @param {String} value The encoded value for the state
14180      */
14181     this.addEvents({
14182         "statechange": true
14183     });
14184     this.state = {};
14185     Roo.state.Provider.superclass.constructor.call(this);
14186 };
14187 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14188     /**
14189      * Returns the current value for a key
14190      * @param {String} name The key name
14191      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14192      * @return {Mixed} The state data
14193      */
14194     get : function(name, defaultValue){
14195         return typeof this.state[name] == "undefined" ?
14196             defaultValue : this.state[name];
14197     },
14198     
14199     /**
14200      * Clears a value from the state
14201      * @param {String} name The key name
14202      */
14203     clear : function(name){
14204         delete this.state[name];
14205         this.fireEvent("statechange", this, name, null);
14206     },
14207     
14208     /**
14209      * Sets the value for a key
14210      * @param {String} name The key name
14211      * @param {Mixed} value The value to set
14212      */
14213     set : function(name, value){
14214         this.state[name] = value;
14215         this.fireEvent("statechange", this, name, value);
14216     },
14217     
14218     /**
14219      * Decodes a string previously encoded with {@link #encodeValue}.
14220      * @param {String} value The value to decode
14221      * @return {Mixed} The decoded value
14222      */
14223     decodeValue : function(cookie){
14224         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14225         var matches = re.exec(unescape(cookie));
14226         if(!matches || !matches[1]) return; // non state cookie
14227         var type = matches[1];
14228         var v = matches[2];
14229         switch(type){
14230             case "n":
14231                 return parseFloat(v);
14232             case "d":
14233                 return new Date(Date.parse(v));
14234             case "b":
14235                 return (v == "1");
14236             case "a":
14237                 var all = [];
14238                 var values = v.split("^");
14239                 for(var i = 0, len = values.length; i < len; i++){
14240                     all.push(this.decodeValue(values[i]));
14241                 }
14242                 return all;
14243            case "o":
14244                 var all = {};
14245                 var values = v.split("^");
14246                 for(var i = 0, len = values.length; i < len; i++){
14247                     var kv = values[i].split("=");
14248                     all[kv[0]] = this.decodeValue(kv[1]);
14249                 }
14250                 return all;
14251            default:
14252                 return v;
14253         }
14254     },
14255     
14256     /**
14257      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14258      * @param {Mixed} value The value to encode
14259      * @return {String} The encoded value
14260      */
14261     encodeValue : function(v){
14262         var enc;
14263         if(typeof v == "number"){
14264             enc = "n:" + v;
14265         }else if(typeof v == "boolean"){
14266             enc = "b:" + (v ? "1" : "0");
14267         }else if(v instanceof Date){
14268             enc = "d:" + v.toGMTString();
14269         }else if(v instanceof Array){
14270             var flat = "";
14271             for(var i = 0, len = v.length; i < len; i++){
14272                 flat += this.encodeValue(v[i]);
14273                 if(i != len-1) flat += "^";
14274             }
14275             enc = "a:" + flat;
14276         }else if(typeof v == "object"){
14277             var flat = "";
14278             for(var key in v){
14279                 if(typeof v[key] != "function"){
14280                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14281                 }
14282             }
14283             enc = "o:" + flat.substring(0, flat.length-1);
14284         }else{
14285             enc = "s:" + v;
14286         }
14287         return escape(enc);        
14288     }
14289 });
14290
14291 /*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301 /**
14302  * @class Roo.state.Manager
14303  * This is the global state manager. By default all components that are "state aware" check this class
14304  * for state information if you don't pass them a custom state provider. In order for this class
14305  * to be useful, it must be initialized with a provider when your application initializes.
14306  <pre><code>
14307 // in your initialization function
14308 init : function(){
14309    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14310    ...
14311    // supposed you have a {@link Roo.BorderLayout}
14312    var layout = new Roo.BorderLayout(...);
14313    layout.restoreState();
14314    // or a {Roo.BasicDialog}
14315    var dialog = new Roo.BasicDialog(...);
14316    dialog.restoreState();
14317  </code></pre>
14318  * @singleton
14319  */
14320 Roo.state.Manager = function(){
14321     var provider = new Roo.state.Provider();
14322     
14323     return {
14324         /**
14325          * Configures the default state provider for your application
14326          * @param {Provider} stateProvider The state provider to set
14327          */
14328         setProvider : function(stateProvider){
14329             provider = stateProvider;
14330         },
14331         
14332         /**
14333          * Returns the current value for a key
14334          * @param {String} name The key name
14335          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14336          * @return {Mixed} The state data
14337          */
14338         get : function(key, defaultValue){
14339             return provider.get(key, defaultValue);
14340         },
14341         
14342         /**
14343          * Sets the value for a key
14344          * @param {String} name The key name
14345          * @param {Mixed} value The state data
14346          */
14347          set : function(key, value){
14348             provider.set(key, value);
14349         },
14350         
14351         /**
14352          * Clears a value from the state
14353          * @param {String} name The key name
14354          */
14355         clear : function(key){
14356             provider.clear(key);
14357         },
14358         
14359         /**
14360          * Gets the currently configured state provider
14361          * @return {Provider} The state provider
14362          */
14363         getProvider : function(){
14364             return provider;
14365         }
14366     };
14367 }();
14368 /*
14369  * Based on:
14370  * Ext JS Library 1.1.1
14371  * Copyright(c) 2006-2007, Ext JS, LLC.
14372  *
14373  * Originally Released Under LGPL - original licence link has changed is not relivant.
14374  *
14375  * Fork - LGPL
14376  * <script type="text/javascript">
14377  */
14378 /**
14379  * @class Roo.state.CookieProvider
14380  * @extends Roo.state.Provider
14381  * The default Provider implementation which saves state via cookies.
14382  * <br />Usage:
14383  <pre><code>
14384    var cp = new Roo.state.CookieProvider({
14385        path: "/cgi-bin/",
14386        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14387        domain: "roojs.com"
14388    })
14389    Roo.state.Manager.setProvider(cp);
14390  </code></pre>
14391  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14392  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14393  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14394  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14395  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14396  * domain the page is running on including the 'www' like 'www.roojs.com')
14397  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14398  * @constructor
14399  * Create a new CookieProvider
14400  * @param {Object} config The configuration object
14401  */
14402 Roo.state.CookieProvider = function(config){
14403     Roo.state.CookieProvider.superclass.constructor.call(this);
14404     this.path = "/";
14405     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14406     this.domain = null;
14407     this.secure = false;
14408     Roo.apply(this, config);
14409     this.state = this.readCookies();
14410 };
14411
14412 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14413     // private
14414     set : function(name, value){
14415         if(typeof value == "undefined" || value === null){
14416             this.clear(name);
14417             return;
14418         }
14419         this.setCookie(name, value);
14420         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14421     },
14422
14423     // private
14424     clear : function(name){
14425         this.clearCookie(name);
14426         Roo.state.CookieProvider.superclass.clear.call(this, name);
14427     },
14428
14429     // private
14430     readCookies : function(){
14431         var cookies = {};
14432         var c = document.cookie + ";";
14433         var re = /\s?(.*?)=(.*?);/g;
14434         var matches;
14435         while((matches = re.exec(c)) != null){
14436             var name = matches[1];
14437             var value = matches[2];
14438             if(name && name.substring(0,3) == "ys-"){
14439                 cookies[name.substr(3)] = this.decodeValue(value);
14440             }
14441         }
14442         return cookies;
14443     },
14444
14445     // private
14446     setCookie : function(name, value){
14447         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14448            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14449            ((this.path == null) ? "" : ("; path=" + this.path)) +
14450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14451            ((this.secure == true) ? "; secure" : "");
14452     },
14453
14454     // private
14455     clearCookie : function(name){
14456         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14457            ((this.path == null) ? "" : ("; path=" + this.path)) +
14458            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14459            ((this.secure == true) ? "; secure" : "");
14460     }
14461 });/*
14462  * Based on:
14463  * Ext JS Library 1.1.1
14464  * Copyright(c) 2006-2007, Ext JS, LLC.
14465  *
14466  * Originally Released Under LGPL - original licence link has changed is not relivant.
14467  *
14468  * Fork - LGPL
14469  * <script type="text/javascript">
14470  */
14471
14472
14473
14474 /*
14475  * These classes are derivatives of the similarly named classes in the YUI Library.
14476  * The original license:
14477  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14478  * Code licensed under the BSD License:
14479  * http://developer.yahoo.net/yui/license.txt
14480  */
14481
14482 (function() {
14483
14484 var Event=Roo.EventManager;
14485 var Dom=Roo.lib.Dom;
14486
14487 /**
14488  * @class Roo.dd.DragDrop
14489  * @extends Roo.util.Observable
14490  * Defines the interface and base operation of items that that can be
14491  * dragged or can be drop targets.  It was designed to be extended, overriding
14492  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14493  * Up to three html elements can be associated with a DragDrop instance:
14494  * <ul>
14495  * <li>linked element: the element that is passed into the constructor.
14496  * This is the element which defines the boundaries for interaction with
14497  * other DragDrop objects.</li>
14498  * <li>handle element(s): The drag operation only occurs if the element that
14499  * was clicked matches a handle element.  By default this is the linked
14500  * element, but there are times that you will want only a portion of the
14501  * linked element to initiate the drag operation, and the setHandleElId()
14502  * method provides a way to define this.</li>
14503  * <li>drag element: this represents the element that would be moved along
14504  * with the cursor during a drag operation.  By default, this is the linked
14505  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14506  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14507  * </li>
14508  * </ul>
14509  * This class should not be instantiated until the onload event to ensure that
14510  * the associated elements are available.
14511  * The following would define a DragDrop obj that would interact with any
14512  * other DragDrop obj in the "group1" group:
14513  * <pre>
14514  *  dd = new Roo.dd.DragDrop("div1", "group1");
14515  * </pre>
14516  * Since none of the event handlers have been implemented, nothing would
14517  * actually happen if you were to run the code above.  Normally you would
14518  * override this class or one of the default implementations, but you can
14519  * also override the methods you want on an instance of the class...
14520  * <pre>
14521  *  dd.onDragDrop = function(e, id) {
14522  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14523  *  }
14524  * </pre>
14525  * @constructor
14526  * @param {String} id of the element that is linked to this instance
14527  * @param {String} sGroup the group of related DragDrop objects
14528  * @param {object} config an object containing configurable attributes
14529  *                Valid properties for DragDrop:
14530  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14531  */
14532 Roo.dd.DragDrop = function(id, sGroup, config) {
14533     if (id) {
14534         this.init(id, sGroup, config);
14535     }
14536     
14537 };
14538
14539 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14540
14541     /**
14542      * The id of the element associated with this object.  This is what we
14543      * refer to as the "linked element" because the size and position of
14544      * this element is used to determine when the drag and drop objects have
14545      * interacted.
14546      * @property id
14547      * @type String
14548      */
14549     id: null,
14550
14551     /**
14552      * Configuration attributes passed into the constructor
14553      * @property config
14554      * @type object
14555      */
14556     config: null,
14557
14558     /**
14559      * The id of the element that will be dragged.  By default this is same
14560      * as the linked element , but could be changed to another element. Ex:
14561      * Roo.dd.DDProxy
14562      * @property dragElId
14563      * @type String
14564      * @private
14565      */
14566     dragElId: null,
14567
14568     /**
14569      * the id of the element that initiates the drag operation.  By default
14570      * this is the linked element, but could be changed to be a child of this
14571      * element.  This lets us do things like only starting the drag when the
14572      * header element within the linked html element is clicked.
14573      * @property handleElId
14574      * @type String
14575      * @private
14576      */
14577     handleElId: null,
14578
14579     /**
14580      * An associative array of HTML tags that will be ignored if clicked.
14581      * @property invalidHandleTypes
14582      * @type {string: string}
14583      */
14584     invalidHandleTypes: null,
14585
14586     /**
14587      * An associative array of ids for elements that will be ignored if clicked
14588      * @property invalidHandleIds
14589      * @type {string: string}
14590      */
14591     invalidHandleIds: null,
14592
14593     /**
14594      * An indexted array of css class names for elements that will be ignored
14595      * if clicked.
14596      * @property invalidHandleClasses
14597      * @type string[]
14598      */
14599     invalidHandleClasses: null,
14600
14601     /**
14602      * The linked element's absolute X position at the time the drag was
14603      * started
14604      * @property startPageX
14605      * @type int
14606      * @private
14607      */
14608     startPageX: 0,
14609
14610     /**
14611      * The linked element's absolute X position at the time the drag was
14612      * started
14613      * @property startPageY
14614      * @type int
14615      * @private
14616      */
14617     startPageY: 0,
14618
14619     /**
14620      * The group defines a logical collection of DragDrop objects that are
14621      * related.  Instances only get events when interacting with other
14622      * DragDrop object in the same group.  This lets us define multiple
14623      * groups using a single DragDrop subclass if we want.
14624      * @property groups
14625      * @type {string: string}
14626      */
14627     groups: null,
14628
14629     /**
14630      * Individual drag/drop instances can be locked.  This will prevent
14631      * onmousedown start drag.
14632      * @property locked
14633      * @type boolean
14634      * @private
14635      */
14636     locked: false,
14637
14638     /**
14639      * Lock this instance
14640      * @method lock
14641      */
14642     lock: function() { this.locked = true; },
14643
14644     /**
14645      * Unlock this instace
14646      * @method unlock
14647      */
14648     unlock: function() { this.locked = false; },
14649
14650     /**
14651      * By default, all insances can be a drop target.  This can be disabled by
14652      * setting isTarget to false.
14653      * @method isTarget
14654      * @type boolean
14655      */
14656     isTarget: true,
14657
14658     /**
14659      * The padding configured for this drag and drop object for calculating
14660      * the drop zone intersection with this object.
14661      * @method padding
14662      * @type int[]
14663      */
14664     padding: null,
14665
14666     /**
14667      * Cached reference to the linked element
14668      * @property _domRef
14669      * @private
14670      */
14671     _domRef: null,
14672
14673     /**
14674      * Internal typeof flag
14675      * @property __ygDragDrop
14676      * @private
14677      */
14678     __ygDragDrop: true,
14679
14680     /**
14681      * Set to true when horizontal contraints are applied
14682      * @property constrainX
14683      * @type boolean
14684      * @private
14685      */
14686     constrainX: false,
14687
14688     /**
14689      * Set to true when vertical contraints are applied
14690      * @property constrainY
14691      * @type boolean
14692      * @private
14693      */
14694     constrainY: false,
14695
14696     /**
14697      * The left constraint
14698      * @property minX
14699      * @type int
14700      * @private
14701      */
14702     minX: 0,
14703
14704     /**
14705      * The right constraint
14706      * @property maxX
14707      * @type int
14708      * @private
14709      */
14710     maxX: 0,
14711
14712     /**
14713      * The up constraint
14714      * @property minY
14715      * @type int
14716      * @type int
14717      * @private
14718      */
14719     minY: 0,
14720
14721     /**
14722      * The down constraint
14723      * @property maxY
14724      * @type int
14725      * @private
14726      */
14727     maxY: 0,
14728
14729     /**
14730      * Maintain offsets when we resetconstraints.  Set to true when you want
14731      * the position of the element relative to its parent to stay the same
14732      * when the page changes
14733      *
14734      * @property maintainOffset
14735      * @type boolean
14736      */
14737     maintainOffset: false,
14738
14739     /**
14740      * Array of pixel locations the element will snap to if we specified a
14741      * horizontal graduation/interval.  This array is generated automatically
14742      * when you define a tick interval.
14743      * @property xTicks
14744      * @type int[]
14745      */
14746     xTicks: null,
14747
14748     /**
14749      * Array of pixel locations the element will snap to if we specified a
14750      * vertical graduation/interval.  This array is generated automatically
14751      * when you define a tick interval.
14752      * @property yTicks
14753      * @type int[]
14754      */
14755     yTicks: null,
14756
14757     /**
14758      * By default the drag and drop instance will only respond to the primary
14759      * button click (left button for a right-handed mouse).  Set to true to
14760      * allow drag and drop to start with any mouse click that is propogated
14761      * by the browser
14762      * @property primaryButtonOnly
14763      * @type boolean
14764      */
14765     primaryButtonOnly: true,
14766
14767     /**
14768      * The availabe property is false until the linked dom element is accessible.
14769      * @property available
14770      * @type boolean
14771      */
14772     available: false,
14773
14774     /**
14775      * By default, drags can only be initiated if the mousedown occurs in the
14776      * region the linked element is.  This is done in part to work around a
14777      * bug in some browsers that mis-report the mousedown if the previous
14778      * mouseup happened outside of the window.  This property is set to true
14779      * if outer handles are defined.
14780      *
14781      * @property hasOuterHandles
14782      * @type boolean
14783      * @default false
14784      */
14785     hasOuterHandles: false,
14786
14787     /**
14788      * Code that executes immediately before the startDrag event
14789      * @method b4StartDrag
14790      * @private
14791      */
14792     b4StartDrag: function(x, y) { },
14793
14794     /**
14795      * Abstract method called after a drag/drop object is clicked
14796      * and the drag or mousedown time thresholds have beeen met.
14797      * @method startDrag
14798      * @param {int} X click location
14799      * @param {int} Y click location
14800      */
14801     startDrag: function(x, y) { /* override this */ },
14802
14803     /**
14804      * Code that executes immediately before the onDrag event
14805      * @method b4Drag
14806      * @private
14807      */
14808     b4Drag: function(e) { },
14809
14810     /**
14811      * Abstract method called during the onMouseMove event while dragging an
14812      * object.
14813      * @method onDrag
14814      * @param {Event} e the mousemove event
14815      */
14816     onDrag: function(e) { /* override this */ },
14817
14818     /**
14819      * Abstract method called when this element fist begins hovering over
14820      * another DragDrop obj
14821      * @method onDragEnter
14822      * @param {Event} e the mousemove event
14823      * @param {String|DragDrop[]} id In POINT mode, the element
14824      * id this is hovering over.  In INTERSECT mode, an array of one or more
14825      * dragdrop items being hovered over.
14826      */
14827     onDragEnter: function(e, id) { /* override this */ },
14828
14829     /**
14830      * Code that executes immediately before the onDragOver event
14831      * @method b4DragOver
14832      * @private
14833      */
14834     b4DragOver: function(e) { },
14835
14836     /**
14837      * Abstract method called when this element is hovering over another
14838      * DragDrop obj
14839      * @method onDragOver
14840      * @param {Event} e the mousemove event
14841      * @param {String|DragDrop[]} id In POINT mode, the element
14842      * id this is hovering over.  In INTERSECT mode, an array of dd items
14843      * being hovered over.
14844      */
14845     onDragOver: function(e, id) { /* override this */ },
14846
14847     /**
14848      * Code that executes immediately before the onDragOut event
14849      * @method b4DragOut
14850      * @private
14851      */
14852     b4DragOut: function(e) { },
14853
14854     /**
14855      * Abstract method called when we are no longer hovering over an element
14856      * @method onDragOut
14857      * @param {Event} e the mousemove event
14858      * @param {String|DragDrop[]} id In POINT mode, the element
14859      * id this was hovering over.  In INTERSECT mode, an array of dd items
14860      * that the mouse is no longer over.
14861      */
14862     onDragOut: function(e, id) { /* override this */ },
14863
14864     /**
14865      * Code that executes immediately before the onDragDrop event
14866      * @method b4DragDrop
14867      * @private
14868      */
14869     b4DragDrop: function(e) { },
14870
14871     /**
14872      * Abstract method called when this item is dropped on another DragDrop
14873      * obj
14874      * @method onDragDrop
14875      * @param {Event} e the mouseup event
14876      * @param {String|DragDrop[]} id In POINT mode, the element
14877      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14878      * was dropped on.
14879      */
14880     onDragDrop: function(e, id) { /* override this */ },
14881
14882     /**
14883      * Abstract method called when this item is dropped on an area with no
14884      * drop target
14885      * @method onInvalidDrop
14886      * @param {Event} e the mouseup event
14887      */
14888     onInvalidDrop: function(e) { /* override this */ },
14889
14890     /**
14891      * Code that executes immediately before the endDrag event
14892      * @method b4EndDrag
14893      * @private
14894      */
14895     b4EndDrag: function(e) { },
14896
14897     /**
14898      * Fired when we are done dragging the object
14899      * @method endDrag
14900      * @param {Event} e the mouseup event
14901      */
14902     endDrag: function(e) { /* override this */ },
14903
14904     /**
14905      * Code executed immediately before the onMouseDown event
14906      * @method b4MouseDown
14907      * @param {Event} e the mousedown event
14908      * @private
14909      */
14910     b4MouseDown: function(e) {  },
14911
14912     /**
14913      * Event handler that fires when a drag/drop obj gets a mousedown
14914      * @method onMouseDown
14915      * @param {Event} e the mousedown event
14916      */
14917     onMouseDown: function(e) { /* override this */ },
14918
14919     /**
14920      * Event handler that fires when a drag/drop obj gets a mouseup
14921      * @method onMouseUp
14922      * @param {Event} e the mouseup event
14923      */
14924     onMouseUp: function(e) { /* override this */ },
14925
14926     /**
14927      * Override the onAvailable method to do what is needed after the initial
14928      * position was determined.
14929      * @method onAvailable
14930      */
14931     onAvailable: function () {
14932     },
14933
14934     /*
14935      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14936      * @type Object
14937      */
14938     defaultPadding : {left:0, right:0, top:0, bottom:0},
14939
14940     /*
14941      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14942  *
14943  * Usage:
14944  <pre><code>
14945  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14946                 { dragElId: "existingProxyDiv" });
14947  dd.startDrag = function(){
14948      this.constrainTo("parent-id");
14949  };
14950  </code></pre>
14951  * Or you can initalize it using the {@link Roo.Element} object:
14952  <pre><code>
14953  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14954      startDrag : function(){
14955          this.constrainTo("parent-id");
14956      }
14957  });
14958  </code></pre>
14959      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14960      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14961      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14962      * an object containing the sides to pad. For example: {right:10, bottom:10}
14963      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14964      */
14965     constrainTo : function(constrainTo, pad, inContent){
14966         if(typeof pad == "number"){
14967             pad = {left: pad, right:pad, top:pad, bottom:pad};
14968         }
14969         pad = pad || this.defaultPadding;
14970         var b = Roo.get(this.getEl()).getBox();
14971         var ce = Roo.get(constrainTo);
14972         var s = ce.getScroll();
14973         var c, cd = ce.dom;
14974         if(cd == document.body){
14975             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14976         }else{
14977             xy = ce.getXY();
14978             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14979         }
14980
14981
14982         var topSpace = b.y - c.y;
14983         var leftSpace = b.x - c.x;
14984
14985         this.resetConstraints();
14986         this.setXConstraint(leftSpace - (pad.left||0), // left
14987                 c.width - leftSpace - b.width - (pad.right||0) //right
14988         );
14989         this.setYConstraint(topSpace - (pad.top||0), //top
14990                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14991         );
14992     },
14993
14994     /**
14995      * Returns a reference to the linked element
14996      * @method getEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getEl: function() {
15000         if (!this._domRef) {
15001             this._domRef = Roo.getDom(this.id);
15002         }
15003
15004         return this._domRef;
15005     },
15006
15007     /**
15008      * Returns a reference to the actual element to drag.  By default this is
15009      * the same as the html element, but it can be assigned to another
15010      * element. An example of this can be found in Roo.dd.DDProxy
15011      * @method getDragEl
15012      * @return {HTMLElement} the html element
15013      */
15014     getDragEl: function() {
15015         return Roo.getDom(this.dragElId);
15016     },
15017
15018     /**
15019      * Sets up the DragDrop object.  Must be called in the constructor of any
15020      * Roo.dd.DragDrop subclass
15021      * @method init
15022      * @param id the id of the linked element
15023      * @param {String} sGroup the group of related items
15024      * @param {object} config configuration attributes
15025      */
15026     init: function(id, sGroup, config) {
15027         this.initTarget(id, sGroup, config);
15028         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15029         // Event.on(this.id, "selectstart", Event.preventDefault);
15030     },
15031
15032     /**
15033      * Initializes Targeting functionality only... the object does not
15034      * get a mousedown handler.
15035      * @method initTarget
15036      * @param id the id of the linked element
15037      * @param {String} sGroup the group of related items
15038      * @param {object} config configuration attributes
15039      */
15040     initTarget: function(id, sGroup, config) {
15041
15042         // configuration attributes
15043         this.config = config || {};
15044
15045         // create a local reference to the drag and drop manager
15046         this.DDM = Roo.dd.DDM;
15047         // initialize the groups array
15048         this.groups = {};
15049
15050         // assume that we have an element reference instead of an id if the
15051         // parameter is not a string
15052         if (typeof id !== "string") {
15053             id = Roo.id(id);
15054         }
15055
15056         // set the id
15057         this.id = id;
15058
15059         // add to an interaction group
15060         this.addToGroup((sGroup) ? sGroup : "default");
15061
15062         // We don't want to register this as the handle with the manager
15063         // so we just set the id rather than calling the setter.
15064         this.handleElId = id;
15065
15066         // the linked element is the element that gets dragged by default
15067         this.setDragElId(id);
15068
15069         // by default, clicked anchors will not start drag operations.
15070         this.invalidHandleTypes = { A: "A" };
15071         this.invalidHandleIds = {};
15072         this.invalidHandleClasses = [];
15073
15074         this.applyConfig();
15075
15076         this.handleOnAvailable();
15077     },
15078
15079     /**
15080      * Applies the configuration parameters that were passed into the constructor.
15081      * This is supposed to happen at each level through the inheritance chain.  So
15082      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15083      * DragDrop in order to get all of the parameters that are available in
15084      * each object.
15085      * @method applyConfig
15086      */
15087     applyConfig: function() {
15088
15089         // configurable properties:
15090         //    padding, isTarget, maintainOffset, primaryButtonOnly
15091         this.padding           = this.config.padding || [0, 0, 0, 0];
15092         this.isTarget          = (this.config.isTarget !== false);
15093         this.maintainOffset    = (this.config.maintainOffset);
15094         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15095
15096     },
15097
15098     /**
15099      * Executed when the linked element is available
15100      * @method handleOnAvailable
15101      * @private
15102      */
15103     handleOnAvailable: function() {
15104         this.available = true;
15105         this.resetConstraints();
15106         this.onAvailable();
15107     },
15108
15109      /**
15110      * Configures the padding for the target zone in px.  Effectively expands
15111      * (or reduces) the virtual object size for targeting calculations.
15112      * Supports css-style shorthand; if only one parameter is passed, all sides
15113      * will have that padding, and if only two are passed, the top and bottom
15114      * will have the first param, the left and right the second.
15115      * @method setPadding
15116      * @param {int} iTop    Top pad
15117      * @param {int} iRight  Right pad
15118      * @param {int} iBot    Bot pad
15119      * @param {int} iLeft   Left pad
15120      */
15121     setPadding: function(iTop, iRight, iBot, iLeft) {
15122         // this.padding = [iLeft, iRight, iTop, iBot];
15123         if (!iRight && 0 !== iRight) {
15124             this.padding = [iTop, iTop, iTop, iTop];
15125         } else if (!iBot && 0 !== iBot) {
15126             this.padding = [iTop, iRight, iTop, iRight];
15127         } else {
15128             this.padding = [iTop, iRight, iBot, iLeft];
15129         }
15130     },
15131
15132     /**
15133      * Stores the initial placement of the linked element.
15134      * @method setInitialPosition
15135      * @param {int} diffX   the X offset, default 0
15136      * @param {int} diffY   the Y offset, default 0
15137      */
15138     setInitPosition: function(diffX, diffY) {
15139         var el = this.getEl();
15140
15141         if (!this.DDM.verifyEl(el)) {
15142             return;
15143         }
15144
15145         var dx = diffX || 0;
15146         var dy = diffY || 0;
15147
15148         var p = Dom.getXY( el );
15149
15150         this.initPageX = p[0] - dx;
15151         this.initPageY = p[1] - dy;
15152
15153         this.lastPageX = p[0];
15154         this.lastPageY = p[1];
15155
15156
15157         this.setStartPosition(p);
15158     },
15159
15160     /**
15161      * Sets the start position of the element.  This is set when the obj
15162      * is initialized, the reset when a drag is started.
15163      * @method setStartPosition
15164      * @param pos current position (from previous lookup)
15165      * @private
15166      */
15167     setStartPosition: function(pos) {
15168         var p = pos || Dom.getXY( this.getEl() );
15169         this.deltaSetXY = null;
15170
15171         this.startPageX = p[0];
15172         this.startPageY = p[1];
15173     },
15174
15175     /**
15176      * Add this instance to a group of related drag/drop objects.  All
15177      * instances belong to at least one group, and can belong to as many
15178      * groups as needed.
15179      * @method addToGroup
15180      * @param sGroup {string} the name of the group
15181      */
15182     addToGroup: function(sGroup) {
15183         this.groups[sGroup] = true;
15184         this.DDM.regDragDrop(this, sGroup);
15185     },
15186
15187     /**
15188      * Remove's this instance from the supplied interaction group
15189      * @method removeFromGroup
15190      * @param {string}  sGroup  The group to drop
15191      */
15192     removeFromGroup: function(sGroup) {
15193         if (this.groups[sGroup]) {
15194             delete this.groups[sGroup];
15195         }
15196
15197         this.DDM.removeDDFromGroup(this, sGroup);
15198     },
15199
15200     /**
15201      * Allows you to specify that an element other than the linked element
15202      * will be moved with the cursor during a drag
15203      * @method setDragElId
15204      * @param id {string} the id of the element that will be used to initiate the drag
15205      */
15206     setDragElId: function(id) {
15207         this.dragElId = id;
15208     },
15209
15210     /**
15211      * Allows you to specify a child of the linked element that should be
15212      * used to initiate the drag operation.  An example of this would be if
15213      * you have a content div with text and links.  Clicking anywhere in the
15214      * content area would normally start the drag operation.  Use this method
15215      * to specify that an element inside of the content div is the element
15216      * that starts the drag operation.
15217      * @method setHandleElId
15218      * @param id {string} the id of the element that will be used to
15219      * initiate the drag.
15220      */
15221     setHandleElId: function(id) {
15222         if (typeof id !== "string") {
15223             id = Roo.id(id);
15224         }
15225         this.handleElId = id;
15226         this.DDM.regHandle(this.id, id);
15227     },
15228
15229     /**
15230      * Allows you to set an element outside of the linked element as a drag
15231      * handle
15232      * @method setOuterHandleElId
15233      * @param id the id of the element that will be used to initiate the drag
15234      */
15235     setOuterHandleElId: function(id) {
15236         if (typeof id !== "string") {
15237             id = Roo.id(id);
15238         }
15239         Event.on(id, "mousedown",
15240                 this.handleMouseDown, this);
15241         this.setHandleElId(id);
15242
15243         this.hasOuterHandles = true;
15244     },
15245
15246     /**
15247      * Remove all drag and drop hooks for this element
15248      * @method unreg
15249      */
15250     unreg: function() {
15251         Event.un(this.id, "mousedown",
15252                 this.handleMouseDown);
15253         this._domRef = null;
15254         this.DDM._remove(this);
15255     },
15256
15257     destroy : function(){
15258         this.unreg();
15259     },
15260
15261     /**
15262      * Returns true if this instance is locked, or the drag drop mgr is locked
15263      * (meaning that all drag/drop is disabled on the page.)
15264      * @method isLocked
15265      * @return {boolean} true if this obj or all drag/drop is locked, else
15266      * false
15267      */
15268     isLocked: function() {
15269         return (this.DDM.isLocked() || this.locked);
15270     },
15271
15272     /**
15273      * Fired when this object is clicked
15274      * @method handleMouseDown
15275      * @param {Event} e
15276      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15277      * @private
15278      */
15279     handleMouseDown: function(e, oDD){
15280         if (this.primaryButtonOnly && e.button != 0) {
15281             return;
15282         }
15283
15284         if (this.isLocked()) {
15285             return;
15286         }
15287
15288         this.DDM.refreshCache(this.groups);
15289
15290         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15291         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15292         } else {
15293             if (this.clickValidator(e)) {
15294
15295                 // set the initial element position
15296                 this.setStartPosition();
15297
15298
15299                 this.b4MouseDown(e);
15300                 this.onMouseDown(e);
15301
15302                 this.DDM.handleMouseDown(e, this);
15303
15304                 this.DDM.stopEvent(e);
15305             } else {
15306
15307
15308             }
15309         }
15310     },
15311
15312     clickValidator: function(e) {
15313         var target = e.getTarget();
15314         return ( this.isValidHandleChild(target) &&
15315                     (this.id == this.handleElId ||
15316                         this.DDM.handleWasClicked(target, this.id)) );
15317     },
15318
15319     /**
15320      * Allows you to specify a tag name that should not start a drag operation
15321      * when clicked.  This is designed to facilitate embedding links within a
15322      * drag handle that do something other than start the drag.
15323      * @method addInvalidHandleType
15324      * @param {string} tagName the type of element to exclude
15325      */
15326     addInvalidHandleType: function(tagName) {
15327         var type = tagName.toUpperCase();
15328         this.invalidHandleTypes[type] = type;
15329     },
15330
15331     /**
15332      * Lets you to specify an element id for a child of a drag handle
15333      * that should not initiate a drag
15334      * @method addInvalidHandleId
15335      * @param {string} id the element id of the element you wish to ignore
15336      */
15337     addInvalidHandleId: function(id) {
15338         if (typeof id !== "string") {
15339             id = Roo.id(id);
15340         }
15341         this.invalidHandleIds[id] = id;
15342     },
15343
15344     /**
15345      * Lets you specify a css class of elements that will not initiate a drag
15346      * @method addInvalidHandleClass
15347      * @param {string} cssClass the class of the elements you wish to ignore
15348      */
15349     addInvalidHandleClass: function(cssClass) {
15350         this.invalidHandleClasses.push(cssClass);
15351     },
15352
15353     /**
15354      * Unsets an excluded tag name set by addInvalidHandleType
15355      * @method removeInvalidHandleType
15356      * @param {string} tagName the type of element to unexclude
15357      */
15358     removeInvalidHandleType: function(tagName) {
15359         var type = tagName.toUpperCase();
15360         // this.invalidHandleTypes[type] = null;
15361         delete this.invalidHandleTypes[type];
15362     },
15363
15364     /**
15365      * Unsets an invalid handle id
15366      * @method removeInvalidHandleId
15367      * @param {string} id the id of the element to re-enable
15368      */
15369     removeInvalidHandleId: function(id) {
15370         if (typeof id !== "string") {
15371             id = Roo.id(id);
15372         }
15373         delete this.invalidHandleIds[id];
15374     },
15375
15376     /**
15377      * Unsets an invalid css class
15378      * @method removeInvalidHandleClass
15379      * @param {string} cssClass the class of the element(s) you wish to
15380      * re-enable
15381      */
15382     removeInvalidHandleClass: function(cssClass) {
15383         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15384             if (this.invalidHandleClasses[i] == cssClass) {
15385                 delete this.invalidHandleClasses[i];
15386             }
15387         }
15388     },
15389
15390     /**
15391      * Checks the tag exclusion list to see if this click should be ignored
15392      * @method isValidHandleChild
15393      * @param {HTMLElement} node the HTMLElement to evaluate
15394      * @return {boolean} true if this is a valid tag type, false if not
15395      */
15396     isValidHandleChild: function(node) {
15397
15398         var valid = true;
15399         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15400         var nodeName;
15401         try {
15402             nodeName = node.nodeName.toUpperCase();
15403         } catch(e) {
15404             nodeName = node.nodeName;
15405         }
15406         valid = valid && !this.invalidHandleTypes[nodeName];
15407         valid = valid && !this.invalidHandleIds[node.id];
15408
15409         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15410             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15411         }
15412
15413
15414         return valid;
15415
15416     },
15417
15418     /**
15419      * Create the array of horizontal tick marks if an interval was specified
15420      * in setXConstraint().
15421      * @method setXTicks
15422      * @private
15423      */
15424     setXTicks: function(iStartX, iTickSize) {
15425         this.xTicks = [];
15426         this.xTickSize = iTickSize;
15427
15428         var tickMap = {};
15429
15430         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15431             if (!tickMap[i]) {
15432                 this.xTicks[this.xTicks.length] = i;
15433                 tickMap[i] = true;
15434             }
15435         }
15436
15437         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15438             if (!tickMap[i]) {
15439                 this.xTicks[this.xTicks.length] = i;
15440                 tickMap[i] = true;
15441             }
15442         }
15443
15444         this.xTicks.sort(this.DDM.numericSort) ;
15445     },
15446
15447     /**
15448      * Create the array of vertical tick marks if an interval was specified in
15449      * setYConstraint().
15450      * @method setYTicks
15451      * @private
15452      */
15453     setYTicks: function(iStartY, iTickSize) {
15454         this.yTicks = [];
15455         this.yTickSize = iTickSize;
15456
15457         var tickMap = {};
15458
15459         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15460             if (!tickMap[i]) {
15461                 this.yTicks[this.yTicks.length] = i;
15462                 tickMap[i] = true;
15463             }
15464         }
15465
15466         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15467             if (!tickMap[i]) {
15468                 this.yTicks[this.yTicks.length] = i;
15469                 tickMap[i] = true;
15470             }
15471         }
15472
15473         this.yTicks.sort(this.DDM.numericSort) ;
15474     },
15475
15476     /**
15477      * By default, the element can be dragged any place on the screen.  Use
15478      * this method to limit the horizontal travel of the element.  Pass in
15479      * 0,0 for the parameters if you want to lock the drag to the y axis.
15480      * @method setXConstraint
15481      * @param {int} iLeft the number of pixels the element can move to the left
15482      * @param {int} iRight the number of pixels the element can move to the
15483      * right
15484      * @param {int} iTickSize optional parameter for specifying that the
15485      * element
15486      * should move iTickSize pixels at a time.
15487      */
15488     setXConstraint: function(iLeft, iRight, iTickSize) {
15489         this.leftConstraint = iLeft;
15490         this.rightConstraint = iRight;
15491
15492         this.minX = this.initPageX - iLeft;
15493         this.maxX = this.initPageX + iRight;
15494         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15495
15496         this.constrainX = true;
15497     },
15498
15499     /**
15500      * Clears any constraints applied to this instance.  Also clears ticks
15501      * since they can't exist independent of a constraint at this time.
15502      * @method clearConstraints
15503      */
15504     clearConstraints: function() {
15505         this.constrainX = false;
15506         this.constrainY = false;
15507         this.clearTicks();
15508     },
15509
15510     /**
15511      * Clears any tick interval defined for this instance
15512      * @method clearTicks
15513      */
15514     clearTicks: function() {
15515         this.xTicks = null;
15516         this.yTicks = null;
15517         this.xTickSize = 0;
15518         this.yTickSize = 0;
15519     },
15520
15521     /**
15522      * By default, the element can be dragged any place on the screen.  Set
15523      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15524      * parameters if you want to lock the drag to the x axis.
15525      * @method setYConstraint
15526      * @param {int} iUp the number of pixels the element can move up
15527      * @param {int} iDown the number of pixels the element can move down
15528      * @param {int} iTickSize optional parameter for specifying that the
15529      * element should move iTickSize pixels at a time.
15530      */
15531     setYConstraint: function(iUp, iDown, iTickSize) {
15532         this.topConstraint = iUp;
15533         this.bottomConstraint = iDown;
15534
15535         this.minY = this.initPageY - iUp;
15536         this.maxY = this.initPageY + iDown;
15537         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15538
15539         this.constrainY = true;
15540
15541     },
15542
15543     /**
15544      * resetConstraints must be called if you manually reposition a dd element.
15545      * @method resetConstraints
15546      * @param {boolean} maintainOffset
15547      */
15548     resetConstraints: function() {
15549
15550
15551         // Maintain offsets if necessary
15552         if (this.initPageX || this.initPageX === 0) {
15553             // figure out how much this thing has moved
15554             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15555             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15556
15557             this.setInitPosition(dx, dy);
15558
15559         // This is the first time we have detected the element's position
15560         } else {
15561             this.setInitPosition();
15562         }
15563
15564         if (this.constrainX) {
15565             this.setXConstraint( this.leftConstraint,
15566                                  this.rightConstraint,
15567                                  this.xTickSize        );
15568         }
15569
15570         if (this.constrainY) {
15571             this.setYConstraint( this.topConstraint,
15572                                  this.bottomConstraint,
15573                                  this.yTickSize         );
15574         }
15575     },
15576
15577     /**
15578      * Normally the drag element is moved pixel by pixel, but we can specify
15579      * that it move a number of pixels at a time.  This method resolves the
15580      * location when we have it set up like this.
15581      * @method getTick
15582      * @param {int} val where we want to place the object
15583      * @param {int[]} tickArray sorted array of valid points
15584      * @return {int} the closest tick
15585      * @private
15586      */
15587     getTick: function(val, tickArray) {
15588
15589         if (!tickArray) {
15590             // If tick interval is not defined, it is effectively 1 pixel,
15591             // so we return the value passed to us.
15592             return val;
15593         } else if (tickArray[0] >= val) {
15594             // The value is lower than the first tick, so we return the first
15595             // tick.
15596             return tickArray[0];
15597         } else {
15598             for (var i=0, len=tickArray.length; i<len; ++i) {
15599                 var next = i + 1;
15600                 if (tickArray[next] && tickArray[next] >= val) {
15601                     var diff1 = val - tickArray[i];
15602                     var diff2 = tickArray[next] - val;
15603                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15604                 }
15605             }
15606
15607             // The value is larger than the last tick, so we return the last
15608             // tick.
15609             return tickArray[tickArray.length - 1];
15610         }
15611     },
15612
15613     /**
15614      * toString method
15615      * @method toString
15616      * @return {string} string representation of the dd obj
15617      */
15618     toString: function() {
15619         return ("DragDrop " + this.id);
15620     }
15621
15622 });
15623
15624 })();
15625 /*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635
15636
15637 /**
15638  * The drag and drop utility provides a framework for building drag and drop
15639  * applications.  In addition to enabling drag and drop for specific elements,
15640  * the drag and drop elements are tracked by the manager class, and the
15641  * interactions between the various elements are tracked during the drag and
15642  * the implementing code is notified about these important moments.
15643  */
15644
15645 // Only load the library once.  Rewriting the manager class would orphan
15646 // existing drag and drop instances.
15647 if (!Roo.dd.DragDropMgr) {
15648
15649 /**
15650  * @class Roo.dd.DragDropMgr
15651  * DragDropMgr is a singleton that tracks the element interaction for
15652  * all DragDrop items in the window.  Generally, you will not call
15653  * this class directly, but it does have helper methods that could
15654  * be useful in your DragDrop implementations.
15655  * @singleton
15656  */
15657 Roo.dd.DragDropMgr = function() {
15658
15659     var Event = Roo.EventManager;
15660
15661     return {
15662
15663         /**
15664          * Two dimensional Array of registered DragDrop objects.  The first
15665          * dimension is the DragDrop item group, the second the DragDrop
15666          * object.
15667          * @property ids
15668          * @type {string: string}
15669          * @private
15670          * @static
15671          */
15672         ids: {},
15673
15674         /**
15675          * Array of element ids defined as drag handles.  Used to determine
15676          * if the element that generated the mousedown event is actually the
15677          * handle and not the html element itself.
15678          * @property handleIds
15679          * @type {string: string}
15680          * @private
15681          * @static
15682          */
15683         handleIds: {},
15684
15685         /**
15686          * the DragDrop object that is currently being dragged
15687          * @property dragCurrent
15688          * @type DragDrop
15689          * @private
15690          * @static
15691          **/
15692         dragCurrent: null,
15693
15694         /**
15695          * the DragDrop object(s) that are being hovered over
15696          * @property dragOvers
15697          * @type Array
15698          * @private
15699          * @static
15700          */
15701         dragOvers: {},
15702
15703         /**
15704          * the X distance between the cursor and the object being dragged
15705          * @property deltaX
15706          * @type int
15707          * @private
15708          * @static
15709          */
15710         deltaX: 0,
15711
15712         /**
15713          * the Y distance between the cursor and the object being dragged
15714          * @property deltaY
15715          * @type int
15716          * @private
15717          * @static
15718          */
15719         deltaY: 0,
15720
15721         /**
15722          * Flag to determine if we should prevent the default behavior of the
15723          * events we define. By default this is true, but this can be set to
15724          * false if you need the default behavior (not recommended)
15725          * @property preventDefault
15726          * @type boolean
15727          * @static
15728          */
15729         preventDefault: true,
15730
15731         /**
15732          * Flag to determine if we should stop the propagation of the events
15733          * we generate. This is true by default but you may want to set it to
15734          * false if the html element contains other features that require the
15735          * mouse click.
15736          * @property stopPropagation
15737          * @type boolean
15738          * @static
15739          */
15740         stopPropagation: true,
15741
15742         /**
15743          * Internal flag that is set to true when drag and drop has been
15744          * intialized
15745          * @property initialized
15746          * @private
15747          * @static
15748          */
15749         initalized: false,
15750
15751         /**
15752          * All drag and drop can be disabled.
15753          * @property locked
15754          * @private
15755          * @static
15756          */
15757         locked: false,
15758
15759         /**
15760          * Called the first time an element is registered.
15761          * @method init
15762          * @private
15763          * @static
15764          */
15765         init: function() {
15766             this.initialized = true;
15767         },
15768
15769         /**
15770          * In point mode, drag and drop interaction is defined by the
15771          * location of the cursor during the drag/drop
15772          * @property POINT
15773          * @type int
15774          * @static
15775          */
15776         POINT: 0,
15777
15778         /**
15779          * In intersect mode, drag and drop interactio nis defined by the
15780          * overlap of two or more drag and drop objects.
15781          * @property INTERSECT
15782          * @type int
15783          * @static
15784          */
15785         INTERSECT: 1,
15786
15787         /**
15788          * The current drag and drop mode.  Default: POINT
15789          * @property mode
15790          * @type int
15791          * @static
15792          */
15793         mode: 0,
15794
15795         /**
15796          * Runs method on all drag and drop objects
15797          * @method _execOnAll
15798          * @private
15799          * @static
15800          */
15801         _execOnAll: function(sMethod, args) {
15802             for (var i in this.ids) {
15803                 for (var j in this.ids[i]) {
15804                     var oDD = this.ids[i][j];
15805                     if (! this.isTypeOfDD(oDD)) {
15806                         continue;
15807                     }
15808                     oDD[sMethod].apply(oDD, args);
15809                 }
15810             }
15811         },
15812
15813         /**
15814          * Drag and drop initialization.  Sets up the global event handlers
15815          * @method _onLoad
15816          * @private
15817          * @static
15818          */
15819         _onLoad: function() {
15820
15821             this.init();
15822
15823
15824             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15825             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15826             Event.on(window,   "unload",    this._onUnload, this, true);
15827             Event.on(window,   "resize",    this._onResize, this, true);
15828             // Event.on(window,   "mouseout",    this._test);
15829
15830         },
15831
15832         /**
15833          * Reset constraints on all drag and drop objs
15834          * @method _onResize
15835          * @private
15836          * @static
15837          */
15838         _onResize: function(e) {
15839             this._execOnAll("resetConstraints", []);
15840         },
15841
15842         /**
15843          * Lock all drag and drop functionality
15844          * @method lock
15845          * @static
15846          */
15847         lock: function() { this.locked = true; },
15848
15849         /**
15850          * Unlock all drag and drop functionality
15851          * @method unlock
15852          * @static
15853          */
15854         unlock: function() { this.locked = false; },
15855
15856         /**
15857          * Is drag and drop locked?
15858          * @method isLocked
15859          * @return {boolean} True if drag and drop is locked, false otherwise.
15860          * @static
15861          */
15862         isLocked: function() { return this.locked; },
15863
15864         /**
15865          * Location cache that is set for all drag drop objects when a drag is
15866          * initiated, cleared when the drag is finished.
15867          * @property locationCache
15868          * @private
15869          * @static
15870          */
15871         locationCache: {},
15872
15873         /**
15874          * Set useCache to false if you want to force object the lookup of each
15875          * drag and drop linked element constantly during a drag.
15876          * @property useCache
15877          * @type boolean
15878          * @static
15879          */
15880         useCache: true,
15881
15882         /**
15883          * The number of pixels that the mouse needs to move after the
15884          * mousedown before the drag is initiated.  Default=3;
15885          * @property clickPixelThresh
15886          * @type int
15887          * @static
15888          */
15889         clickPixelThresh: 3,
15890
15891         /**
15892          * The number of milliseconds after the mousedown event to initiate the
15893          * drag if we don't get a mouseup event. Default=1000
15894          * @property clickTimeThresh
15895          * @type int
15896          * @static
15897          */
15898         clickTimeThresh: 350,
15899
15900         /**
15901          * Flag that indicates that either the drag pixel threshold or the
15902          * mousdown time threshold has been met
15903          * @property dragThreshMet
15904          * @type boolean
15905          * @private
15906          * @static
15907          */
15908         dragThreshMet: false,
15909
15910         /**
15911          * Timeout used for the click time threshold
15912          * @property clickTimeout
15913          * @type Object
15914          * @private
15915          * @static
15916          */
15917         clickTimeout: null,
15918
15919         /**
15920          * The X position of the mousedown event stored for later use when a
15921          * drag threshold is met.
15922          * @property startX
15923          * @type int
15924          * @private
15925          * @static
15926          */
15927         startX: 0,
15928
15929         /**
15930          * The Y position of the mousedown event stored for later use when a
15931          * drag threshold is met.
15932          * @property startY
15933          * @type int
15934          * @private
15935          * @static
15936          */
15937         startY: 0,
15938
15939         /**
15940          * Each DragDrop instance must be registered with the DragDropMgr.
15941          * This is executed in DragDrop.init()
15942          * @method regDragDrop
15943          * @param {DragDrop} oDD the DragDrop object to register
15944          * @param {String} sGroup the name of the group this element belongs to
15945          * @static
15946          */
15947         regDragDrop: function(oDD, sGroup) {
15948             if (!this.initialized) { this.init(); }
15949
15950             if (!this.ids[sGroup]) {
15951                 this.ids[sGroup] = {};
15952             }
15953             this.ids[sGroup][oDD.id] = oDD;
15954         },
15955
15956         /**
15957          * Removes the supplied dd instance from the supplied group. Executed
15958          * by DragDrop.removeFromGroup, so don't call this function directly.
15959          * @method removeDDFromGroup
15960          * @private
15961          * @static
15962          */
15963         removeDDFromGroup: function(oDD, sGroup) {
15964             if (!this.ids[sGroup]) {
15965                 this.ids[sGroup] = {};
15966             }
15967
15968             var obj = this.ids[sGroup];
15969             if (obj && obj[oDD.id]) {
15970                 delete obj[oDD.id];
15971             }
15972         },
15973
15974         /**
15975          * Unregisters a drag and drop item.  This is executed in
15976          * DragDrop.unreg, use that method instead of calling this directly.
15977          * @method _remove
15978          * @private
15979          * @static
15980          */
15981         _remove: function(oDD) {
15982             for (var g in oDD.groups) {
15983                 if (g && this.ids[g][oDD.id]) {
15984                     delete this.ids[g][oDD.id];
15985                 }
15986             }
15987             delete this.handleIds[oDD.id];
15988         },
15989
15990         /**
15991          * Each DragDrop handle element must be registered.  This is done
15992          * automatically when executing DragDrop.setHandleElId()
15993          * @method regHandle
15994          * @param {String} sDDId the DragDrop id this element is a handle for
15995          * @param {String} sHandleId the id of the element that is the drag
15996          * handle
15997          * @static
15998          */
15999         regHandle: function(sDDId, sHandleId) {
16000             if (!this.handleIds[sDDId]) {
16001                 this.handleIds[sDDId] = {};
16002             }
16003             this.handleIds[sDDId][sHandleId] = sHandleId;
16004         },
16005
16006         /**
16007          * Utility function to determine if a given element has been
16008          * registered as a drag drop item.
16009          * @method isDragDrop
16010          * @param {String} id the element id to check
16011          * @return {boolean} true if this element is a DragDrop item,
16012          * false otherwise
16013          * @static
16014          */
16015         isDragDrop: function(id) {
16016             return ( this.getDDById(id) ) ? true : false;
16017         },
16018
16019         /**
16020          * Returns the drag and drop instances that are in all groups the
16021          * passed in instance belongs to.
16022          * @method getRelated
16023          * @param {DragDrop} p_oDD the obj to get related data for
16024          * @param {boolean} bTargetsOnly if true, only return targetable objs
16025          * @return {DragDrop[]} the related instances
16026          * @static
16027          */
16028         getRelated: function(p_oDD, bTargetsOnly) {
16029             var oDDs = [];
16030             for (var i in p_oDD.groups) {
16031                 for (j in this.ids[i]) {
16032                     var dd = this.ids[i][j];
16033                     if (! this.isTypeOfDD(dd)) {
16034                         continue;
16035                     }
16036                     if (!bTargetsOnly || dd.isTarget) {
16037                         oDDs[oDDs.length] = dd;
16038                     }
16039                 }
16040             }
16041
16042             return oDDs;
16043         },
16044
16045         /**
16046          * Returns true if the specified dd target is a legal target for
16047          * the specifice drag obj
16048          * @method isLegalTarget
16049          * @param {DragDrop} the drag obj
16050          * @param {DragDrop} the target
16051          * @return {boolean} true if the target is a legal target for the
16052          * dd obj
16053          * @static
16054          */
16055         isLegalTarget: function (oDD, oTargetDD) {
16056             var targets = this.getRelated(oDD, true);
16057             for (var i=0, len=targets.length;i<len;++i) {
16058                 if (targets[i].id == oTargetDD.id) {
16059                     return true;
16060                 }
16061             }
16062
16063             return false;
16064         },
16065
16066         /**
16067          * My goal is to be able to transparently determine if an object is
16068          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16069          * returns "object", oDD.constructor.toString() always returns
16070          * "DragDrop" and not the name of the subclass.  So for now it just
16071          * evaluates a well-known variable in DragDrop.
16072          * @method isTypeOfDD
16073          * @param {Object} the object to evaluate
16074          * @return {boolean} true if typeof oDD = DragDrop
16075          * @static
16076          */
16077         isTypeOfDD: function (oDD) {
16078             return (oDD && oDD.__ygDragDrop);
16079         },
16080
16081         /**
16082          * Utility function to determine if a given element has been
16083          * registered as a drag drop handle for the given Drag Drop object.
16084          * @method isHandle
16085          * @param {String} id the element id to check
16086          * @return {boolean} true if this element is a DragDrop handle, false
16087          * otherwise
16088          * @static
16089          */
16090         isHandle: function(sDDId, sHandleId) {
16091             return ( this.handleIds[sDDId] &&
16092                             this.handleIds[sDDId][sHandleId] );
16093         },
16094
16095         /**
16096          * Returns the DragDrop instance for a given id
16097          * @method getDDById
16098          * @param {String} id the id of the DragDrop object
16099          * @return {DragDrop} the drag drop object, null if it is not found
16100          * @static
16101          */
16102         getDDById: function(id) {
16103             for (var i in this.ids) {
16104                 if (this.ids[i][id]) {
16105                     return this.ids[i][id];
16106                 }
16107             }
16108             return null;
16109         },
16110
16111         /**
16112          * Fired after a registered DragDrop object gets the mousedown event.
16113          * Sets up the events required to track the object being dragged
16114          * @method handleMouseDown
16115          * @param {Event} e the event
16116          * @param oDD the DragDrop object being dragged
16117          * @private
16118          * @static
16119          */
16120         handleMouseDown: function(e, oDD) {
16121             if(Roo.QuickTips){
16122                 Roo.QuickTips.disable();
16123             }
16124             this.currentTarget = e.getTarget();
16125
16126             this.dragCurrent = oDD;
16127
16128             var el = oDD.getEl();
16129
16130             // track start position
16131             this.startX = e.getPageX();
16132             this.startY = e.getPageY();
16133
16134             this.deltaX = this.startX - el.offsetLeft;
16135             this.deltaY = this.startY - el.offsetTop;
16136
16137             this.dragThreshMet = false;
16138
16139             this.clickTimeout = setTimeout(
16140                     function() {
16141                         var DDM = Roo.dd.DDM;
16142                         DDM.startDrag(DDM.startX, DDM.startY);
16143                     },
16144                     this.clickTimeThresh );
16145         },
16146
16147         /**
16148          * Fired when either the drag pixel threshol or the mousedown hold
16149          * time threshold has been met.
16150          * @method startDrag
16151          * @param x {int} the X position of the original mousedown
16152          * @param y {int} the Y position of the original mousedown
16153          * @static
16154          */
16155         startDrag: function(x, y) {
16156             clearTimeout(this.clickTimeout);
16157             if (this.dragCurrent) {
16158                 this.dragCurrent.b4StartDrag(x, y);
16159                 this.dragCurrent.startDrag(x, y);
16160             }
16161             this.dragThreshMet = true;
16162         },
16163
16164         /**
16165          * Internal function to handle the mouseup event.  Will be invoked
16166          * from the context of the document.
16167          * @method handleMouseUp
16168          * @param {Event} e the event
16169          * @private
16170          * @static
16171          */
16172         handleMouseUp: function(e) {
16173
16174             if(Roo.QuickTips){
16175                 Roo.QuickTips.enable();
16176             }
16177             if (! this.dragCurrent) {
16178                 return;
16179             }
16180
16181             clearTimeout(this.clickTimeout);
16182
16183             if (this.dragThreshMet) {
16184                 this.fireEvents(e, true);
16185             } else {
16186             }
16187
16188             this.stopDrag(e);
16189
16190             this.stopEvent(e);
16191         },
16192
16193         /**
16194          * Utility to stop event propagation and event default, if these
16195          * features are turned on.
16196          * @method stopEvent
16197          * @param {Event} e the event as returned by this.getEvent()
16198          * @static
16199          */
16200         stopEvent: function(e){
16201             if(this.stopPropagation) {
16202                 e.stopPropagation();
16203             }
16204
16205             if (this.preventDefault) {
16206                 e.preventDefault();
16207             }
16208         },
16209
16210         /**
16211          * Internal function to clean up event handlers after the drag
16212          * operation is complete
16213          * @method stopDrag
16214          * @param {Event} e the event
16215          * @private
16216          * @static
16217          */
16218         stopDrag: function(e) {
16219             // Fire the drag end event for the item that was dragged
16220             if (this.dragCurrent) {
16221                 if (this.dragThreshMet) {
16222                     this.dragCurrent.b4EndDrag(e);
16223                     this.dragCurrent.endDrag(e);
16224                 }
16225
16226                 this.dragCurrent.onMouseUp(e);
16227             }
16228
16229             this.dragCurrent = null;
16230             this.dragOvers = {};
16231         },
16232
16233         /**
16234          * Internal function to handle the mousemove event.  Will be invoked
16235          * from the context of the html element.
16236          *
16237          * @TODO figure out what we can do about mouse events lost when the
16238          * user drags objects beyond the window boundary.  Currently we can
16239          * detect this in internet explorer by verifying that the mouse is
16240          * down during the mousemove event.  Firefox doesn't give us the
16241          * button state on the mousemove event.
16242          * @method handleMouseMove
16243          * @param {Event} e the event
16244          * @private
16245          * @static
16246          */
16247         handleMouseMove: function(e) {
16248             if (! this.dragCurrent) {
16249                 return true;
16250             }
16251
16252             // var button = e.which || e.button;
16253
16254             // check for IE mouseup outside of page boundary
16255             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16256                 this.stopEvent(e);
16257                 return this.handleMouseUp(e);
16258             }
16259
16260             if (!this.dragThreshMet) {
16261                 var diffX = Math.abs(this.startX - e.getPageX());
16262                 var diffY = Math.abs(this.startY - e.getPageY());
16263                 if (diffX > this.clickPixelThresh ||
16264                             diffY > this.clickPixelThresh) {
16265                     this.startDrag(this.startX, this.startY);
16266                 }
16267             }
16268
16269             if (this.dragThreshMet) {
16270                 this.dragCurrent.b4Drag(e);
16271                 this.dragCurrent.onDrag(e);
16272                 if(!this.dragCurrent.moveOnly){
16273                     this.fireEvents(e, false);
16274                 }
16275             }
16276
16277             this.stopEvent(e);
16278
16279             return true;
16280         },
16281
16282         /**
16283          * Iterates over all of the DragDrop elements to find ones we are
16284          * hovering over or dropping on
16285          * @method fireEvents
16286          * @param {Event} e the event
16287          * @param {boolean} isDrop is this a drop op or a mouseover op?
16288          * @private
16289          * @static
16290          */
16291         fireEvents: function(e, isDrop) {
16292             var dc = this.dragCurrent;
16293
16294             // If the user did the mouse up outside of the window, we could
16295             // get here even though we have ended the drag.
16296             if (!dc || dc.isLocked()) {
16297                 return;
16298             }
16299
16300             var pt = e.getPoint();
16301
16302             // cache the previous dragOver array
16303             var oldOvers = [];
16304
16305             var outEvts   = [];
16306             var overEvts  = [];
16307             var dropEvts  = [];
16308             var enterEvts = [];
16309
16310             // Check to see if the object(s) we were hovering over is no longer
16311             // being hovered over so we can fire the onDragOut event
16312             for (var i in this.dragOvers) {
16313
16314                 var ddo = this.dragOvers[i];
16315
16316                 if (! this.isTypeOfDD(ddo)) {
16317                     continue;
16318                 }
16319
16320                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16321                     outEvts.push( ddo );
16322                 }
16323
16324                 oldOvers[i] = true;
16325                 delete this.dragOvers[i];
16326             }
16327
16328             for (var sGroup in dc.groups) {
16329
16330                 if ("string" != typeof sGroup) {
16331                     continue;
16332                 }
16333
16334                 for (i in this.ids[sGroup]) {
16335                     var oDD = this.ids[sGroup][i];
16336                     if (! this.isTypeOfDD(oDD)) {
16337                         continue;
16338                     }
16339
16340                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16341                         if (this.isOverTarget(pt, oDD, this.mode)) {
16342                             // look for drop interactions
16343                             if (isDrop) {
16344                                 dropEvts.push( oDD );
16345                             // look for drag enter and drag over interactions
16346                             } else {
16347
16348                                 // initial drag over: dragEnter fires
16349                                 if (!oldOvers[oDD.id]) {
16350                                     enterEvts.push( oDD );
16351                                 // subsequent drag overs: dragOver fires
16352                                 } else {
16353                                     overEvts.push( oDD );
16354                                 }
16355
16356                                 this.dragOvers[oDD.id] = oDD;
16357                             }
16358                         }
16359                     }
16360                 }
16361             }
16362
16363             if (this.mode) {
16364                 if (outEvts.length) {
16365                     dc.b4DragOut(e, outEvts);
16366                     dc.onDragOut(e, outEvts);
16367                 }
16368
16369                 if (enterEvts.length) {
16370                     dc.onDragEnter(e, enterEvts);
16371                 }
16372
16373                 if (overEvts.length) {
16374                     dc.b4DragOver(e, overEvts);
16375                     dc.onDragOver(e, overEvts);
16376                 }
16377
16378                 if (dropEvts.length) {
16379                     dc.b4DragDrop(e, dropEvts);
16380                     dc.onDragDrop(e, dropEvts);
16381                 }
16382
16383             } else {
16384                 // fire dragout events
16385                 var len = 0;
16386                 for (i=0, len=outEvts.length; i<len; ++i) {
16387                     dc.b4DragOut(e, outEvts[i].id);
16388                     dc.onDragOut(e, outEvts[i].id);
16389                 }
16390
16391                 // fire enter events
16392                 for (i=0,len=enterEvts.length; i<len; ++i) {
16393                     // dc.b4DragEnter(e, oDD.id);
16394                     dc.onDragEnter(e, enterEvts[i].id);
16395                 }
16396
16397                 // fire over events
16398                 for (i=0,len=overEvts.length; i<len; ++i) {
16399                     dc.b4DragOver(e, overEvts[i].id);
16400                     dc.onDragOver(e, overEvts[i].id);
16401                 }
16402
16403                 // fire drop events
16404                 for (i=0, len=dropEvts.length; i<len; ++i) {
16405                     dc.b4DragDrop(e, dropEvts[i].id);
16406                     dc.onDragDrop(e, dropEvts[i].id);
16407                 }
16408
16409             }
16410
16411             // notify about a drop that did not find a target
16412             if (isDrop && !dropEvts.length) {
16413                 dc.onInvalidDrop(e);
16414             }
16415
16416         },
16417
16418         /**
16419          * Helper function for getting the best match from the list of drag
16420          * and drop objects returned by the drag and drop events when we are
16421          * in INTERSECT mode.  It returns either the first object that the
16422          * cursor is over, or the object that has the greatest overlap with
16423          * the dragged element.
16424          * @method getBestMatch
16425          * @param  {DragDrop[]} dds The array of drag and drop objects
16426          * targeted
16427          * @return {DragDrop}       The best single match
16428          * @static
16429          */
16430         getBestMatch: function(dds) {
16431             var winner = null;
16432             // Return null if the input is not what we expect
16433             //if (!dds || !dds.length || dds.length == 0) {
16434                // winner = null;
16435             // If there is only one item, it wins
16436             //} else if (dds.length == 1) {
16437
16438             var len = dds.length;
16439
16440             if (len == 1) {
16441                 winner = dds[0];
16442             } else {
16443                 // Loop through the targeted items
16444                 for (var i=0; i<len; ++i) {
16445                     var dd = dds[i];
16446                     // If the cursor is over the object, it wins.  If the
16447                     // cursor is over multiple matches, the first one we come
16448                     // to wins.
16449                     if (dd.cursorIsOver) {
16450                         winner = dd;
16451                         break;
16452                     // Otherwise the object with the most overlap wins
16453                     } else {
16454                         if (!winner ||
16455                             winner.overlap.getArea() < dd.overlap.getArea()) {
16456                             winner = dd;
16457                         }
16458                     }
16459                 }
16460             }
16461
16462             return winner;
16463         },
16464
16465         /**
16466          * Refreshes the cache of the top-left and bottom-right points of the
16467          * drag and drop objects in the specified group(s).  This is in the
16468          * format that is stored in the drag and drop instance, so typical
16469          * usage is:
16470          * <code>
16471          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16472          * </code>
16473          * Alternatively:
16474          * <code>
16475          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16476          * </code>
16477          * @TODO this really should be an indexed array.  Alternatively this
16478          * method could accept both.
16479          * @method refreshCache
16480          * @param {Object} groups an associative array of groups to refresh
16481          * @static
16482          */
16483         refreshCache: function(groups) {
16484             for (var sGroup in groups) {
16485                 if ("string" != typeof sGroup) {
16486                     continue;
16487                 }
16488                 for (var i in this.ids[sGroup]) {
16489                     var oDD = this.ids[sGroup][i];
16490
16491                     if (this.isTypeOfDD(oDD)) {
16492                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16493                         var loc = this.getLocation(oDD);
16494                         if (loc) {
16495                             this.locationCache[oDD.id] = loc;
16496                         } else {
16497                             delete this.locationCache[oDD.id];
16498                             // this will unregister the drag and drop object if
16499                             // the element is not in a usable state
16500                             // oDD.unreg();
16501                         }
16502                     }
16503                 }
16504             }
16505         },
16506
16507         /**
16508          * This checks to make sure an element exists and is in the DOM.  The
16509          * main purpose is to handle cases where innerHTML is used to remove
16510          * drag and drop objects from the DOM.  IE provides an 'unspecified
16511          * error' when trying to access the offsetParent of such an element
16512          * @method verifyEl
16513          * @param {HTMLElement} el the element to check
16514          * @return {boolean} true if the element looks usable
16515          * @static
16516          */
16517         verifyEl: function(el) {
16518             if (el) {
16519                 var parent;
16520                 if(Roo.isIE){
16521                     try{
16522                         parent = el.offsetParent;
16523                     }catch(e){}
16524                 }else{
16525                     parent = el.offsetParent;
16526                 }
16527                 if (parent) {
16528                     return true;
16529                 }
16530             }
16531
16532             return false;
16533         },
16534
16535         /**
16536          * Returns a Region object containing the drag and drop element's position
16537          * and size, including the padding configured for it
16538          * @method getLocation
16539          * @param {DragDrop} oDD the drag and drop object to get the
16540          *                       location for
16541          * @return {Roo.lib.Region} a Region object representing the total area
16542          *                             the element occupies, including any padding
16543          *                             the instance is configured for.
16544          * @static
16545          */
16546         getLocation: function(oDD) {
16547             if (! this.isTypeOfDD(oDD)) {
16548                 return null;
16549             }
16550
16551             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16552
16553             try {
16554                 pos= Roo.lib.Dom.getXY(el);
16555             } catch (e) { }
16556
16557             if (!pos) {
16558                 return null;
16559             }
16560
16561             x1 = pos[0];
16562             x2 = x1 + el.offsetWidth;
16563             y1 = pos[1];
16564             y2 = y1 + el.offsetHeight;
16565
16566             t = y1 - oDD.padding[0];
16567             r = x2 + oDD.padding[1];
16568             b = y2 + oDD.padding[2];
16569             l = x1 - oDD.padding[3];
16570
16571             return new Roo.lib.Region( t, r, b, l );
16572         },
16573
16574         /**
16575          * Checks the cursor location to see if it over the target
16576          * @method isOverTarget
16577          * @param {Roo.lib.Point} pt The point to evaluate
16578          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16579          * @return {boolean} true if the mouse is over the target
16580          * @private
16581          * @static
16582          */
16583         isOverTarget: function(pt, oTarget, intersect) {
16584             // use cache if available
16585             var loc = this.locationCache[oTarget.id];
16586             if (!loc || !this.useCache) {
16587                 loc = this.getLocation(oTarget);
16588                 this.locationCache[oTarget.id] = loc;
16589
16590             }
16591
16592             if (!loc) {
16593                 return false;
16594             }
16595
16596             oTarget.cursorIsOver = loc.contains( pt );
16597
16598             // DragDrop is using this as a sanity check for the initial mousedown
16599             // in this case we are done.  In POINT mode, if the drag obj has no
16600             // contraints, we are also done. Otherwise we need to evaluate the
16601             // location of the target as related to the actual location of the
16602             // dragged element.
16603             var dc = this.dragCurrent;
16604             if (!dc || !dc.getTargetCoord ||
16605                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16606                 return oTarget.cursorIsOver;
16607             }
16608
16609             oTarget.overlap = null;
16610
16611             // Get the current location of the drag element, this is the
16612             // location of the mouse event less the delta that represents
16613             // where the original mousedown happened on the element.  We
16614             // need to consider constraints and ticks as well.
16615             var pos = dc.getTargetCoord(pt.x, pt.y);
16616
16617             var el = dc.getDragEl();
16618             var curRegion = new Roo.lib.Region( pos.y,
16619                                                    pos.x + el.offsetWidth,
16620                                                    pos.y + el.offsetHeight,
16621                                                    pos.x );
16622
16623             var overlap = curRegion.intersect(loc);
16624
16625             if (overlap) {
16626                 oTarget.overlap = overlap;
16627                 return (intersect) ? true : oTarget.cursorIsOver;
16628             } else {
16629                 return false;
16630             }
16631         },
16632
16633         /**
16634          * unload event handler
16635          * @method _onUnload
16636          * @private
16637          * @static
16638          */
16639         _onUnload: function(e, me) {
16640             Roo.dd.DragDropMgr.unregAll();
16641         },
16642
16643         /**
16644          * Cleans up the drag and drop events and objects.
16645          * @method unregAll
16646          * @private
16647          * @static
16648          */
16649         unregAll: function() {
16650
16651             if (this.dragCurrent) {
16652                 this.stopDrag();
16653                 this.dragCurrent = null;
16654             }
16655
16656             this._execOnAll("unreg", []);
16657
16658             for (i in this.elementCache) {
16659                 delete this.elementCache[i];
16660             }
16661
16662             this.elementCache = {};
16663             this.ids = {};
16664         },
16665
16666         /**
16667          * A cache of DOM elements
16668          * @property elementCache
16669          * @private
16670          * @static
16671          */
16672         elementCache: {},
16673
16674         /**
16675          * Get the wrapper for the DOM element specified
16676          * @method getElWrapper
16677          * @param {String} id the id of the element to get
16678          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16679          * @private
16680          * @deprecated This wrapper isn't that useful
16681          * @static
16682          */
16683         getElWrapper: function(id) {
16684             var oWrapper = this.elementCache[id];
16685             if (!oWrapper || !oWrapper.el) {
16686                 oWrapper = this.elementCache[id] =
16687                     new this.ElementWrapper(Roo.getDom(id));
16688             }
16689             return oWrapper;
16690         },
16691
16692         /**
16693          * Returns the actual DOM element
16694          * @method getElement
16695          * @param {String} id the id of the elment to get
16696          * @return {Object} The element
16697          * @deprecated use Roo.getDom instead
16698          * @static
16699          */
16700         getElement: function(id) {
16701             return Roo.getDom(id);
16702         },
16703
16704         /**
16705          * Returns the style property for the DOM element (i.e.,
16706          * document.getElById(id).style)
16707          * @method getCss
16708          * @param {String} id the id of the elment to get
16709          * @return {Object} The style property of the element
16710          * @deprecated use Roo.getDom instead
16711          * @static
16712          */
16713         getCss: function(id) {
16714             var el = Roo.getDom(id);
16715             return (el) ? el.style : null;
16716         },
16717
16718         /**
16719          * Inner class for cached elements
16720          * @class DragDropMgr.ElementWrapper
16721          * @for DragDropMgr
16722          * @private
16723          * @deprecated
16724          */
16725         ElementWrapper: function(el) {
16726                 /**
16727                  * The element
16728                  * @property el
16729                  */
16730                 this.el = el || null;
16731                 /**
16732                  * The element id
16733                  * @property id
16734                  */
16735                 this.id = this.el && el.id;
16736                 /**
16737                  * A reference to the style property
16738                  * @property css
16739                  */
16740                 this.css = this.el && el.style;
16741             },
16742
16743         /**
16744          * Returns the X position of an html element
16745          * @method getPosX
16746          * @param el the element for which to get the position
16747          * @return {int} the X coordinate
16748          * @for DragDropMgr
16749          * @deprecated use Roo.lib.Dom.getX instead
16750          * @static
16751          */
16752         getPosX: function(el) {
16753             return Roo.lib.Dom.getX(el);
16754         },
16755
16756         /**
16757          * Returns the Y position of an html element
16758          * @method getPosY
16759          * @param el the element for which to get the position
16760          * @return {int} the Y coordinate
16761          * @deprecated use Roo.lib.Dom.getY instead
16762          * @static
16763          */
16764         getPosY: function(el) {
16765             return Roo.lib.Dom.getY(el);
16766         },
16767
16768         /**
16769          * Swap two nodes.  In IE, we use the native method, for others we
16770          * emulate the IE behavior
16771          * @method swapNode
16772          * @param n1 the first node to swap
16773          * @param n2 the other node to swap
16774          * @static
16775          */
16776         swapNode: function(n1, n2) {
16777             if (n1.swapNode) {
16778                 n1.swapNode(n2);
16779             } else {
16780                 var p = n2.parentNode;
16781                 var s = n2.nextSibling;
16782
16783                 if (s == n1) {
16784                     p.insertBefore(n1, n2);
16785                 } else if (n2 == n1.nextSibling) {
16786                     p.insertBefore(n2, n1);
16787                 } else {
16788                     n1.parentNode.replaceChild(n2, n1);
16789                     p.insertBefore(n1, s);
16790                 }
16791             }
16792         },
16793
16794         /**
16795          * Returns the current scroll position
16796          * @method getScroll
16797          * @private
16798          * @static
16799          */
16800         getScroll: function () {
16801             var t, l, dde=document.documentElement, db=document.body;
16802             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16803                 t = dde.scrollTop;
16804                 l = dde.scrollLeft;
16805             } else if (db) {
16806                 t = db.scrollTop;
16807                 l = db.scrollLeft;
16808             } else {
16809
16810             }
16811             return { top: t, left: l };
16812         },
16813
16814         /**
16815          * Returns the specified element style property
16816          * @method getStyle
16817          * @param {HTMLElement} el          the element
16818          * @param {string}      styleProp   the style property
16819          * @return {string} The value of the style property
16820          * @deprecated use Roo.lib.Dom.getStyle
16821          * @static
16822          */
16823         getStyle: function(el, styleProp) {
16824             return Roo.fly(el).getStyle(styleProp);
16825         },
16826
16827         /**
16828          * Gets the scrollTop
16829          * @method getScrollTop
16830          * @return {int} the document's scrollTop
16831          * @static
16832          */
16833         getScrollTop: function () { return this.getScroll().top; },
16834
16835         /**
16836          * Gets the scrollLeft
16837          * @method getScrollLeft
16838          * @return {int} the document's scrollTop
16839          * @static
16840          */
16841         getScrollLeft: function () { return this.getScroll().left; },
16842
16843         /**
16844          * Sets the x/y position of an element to the location of the
16845          * target element.
16846          * @method moveToEl
16847          * @param {HTMLElement} moveEl      The element to move
16848          * @param {HTMLElement} targetEl    The position reference element
16849          * @static
16850          */
16851         moveToEl: function (moveEl, targetEl) {
16852             var aCoord = Roo.lib.Dom.getXY(targetEl);
16853             Roo.lib.Dom.setXY(moveEl, aCoord);
16854         },
16855
16856         /**
16857          * Numeric array sort function
16858          * @method numericSort
16859          * @static
16860          */
16861         numericSort: function(a, b) { return (a - b); },
16862
16863         /**
16864          * Internal counter
16865          * @property _timeoutCount
16866          * @private
16867          * @static
16868          */
16869         _timeoutCount: 0,
16870
16871         /**
16872          * Trying to make the load order less important.  Without this we get
16873          * an error if this file is loaded before the Event Utility.
16874          * @method _addListeners
16875          * @private
16876          * @static
16877          */
16878         _addListeners: function() {
16879             var DDM = Roo.dd.DDM;
16880             if ( Roo.lib.Event && document ) {
16881                 DDM._onLoad();
16882             } else {
16883                 if (DDM._timeoutCount > 2000) {
16884                 } else {
16885                     setTimeout(DDM._addListeners, 10);
16886                     if (document && document.body) {
16887                         DDM._timeoutCount += 1;
16888                     }
16889                 }
16890             }
16891         },
16892
16893         /**
16894          * Recursively searches the immediate parent and all child nodes for
16895          * the handle element in order to determine wheter or not it was
16896          * clicked.
16897          * @method handleWasClicked
16898          * @param node the html element to inspect
16899          * @static
16900          */
16901         handleWasClicked: function(node, id) {
16902             if (this.isHandle(id, node.id)) {
16903                 return true;
16904             } else {
16905                 // check to see if this is a text node child of the one we want
16906                 var p = node.parentNode;
16907
16908                 while (p) {
16909                     if (this.isHandle(id, p.id)) {
16910                         return true;
16911                     } else {
16912                         p = p.parentNode;
16913                     }
16914                 }
16915             }
16916
16917             return false;
16918         }
16919
16920     };
16921
16922 }();
16923
16924 // shorter alias, save a few bytes
16925 Roo.dd.DDM = Roo.dd.DragDropMgr;
16926 Roo.dd.DDM._addListeners();
16927
16928 }/*
16929  * Based on:
16930  * Ext JS Library 1.1.1
16931  * Copyright(c) 2006-2007, Ext JS, LLC.
16932  *
16933  * Originally Released Under LGPL - original licence link has changed is not relivant.
16934  *
16935  * Fork - LGPL
16936  * <script type="text/javascript">
16937  */
16938
16939 /**
16940  * @class Roo.dd.DD
16941  * A DragDrop implementation where the linked element follows the
16942  * mouse cursor during a drag.
16943  * @extends Roo.dd.DragDrop
16944  * @constructor
16945  * @param {String} id the id of the linked element
16946  * @param {String} sGroup the group of related DragDrop items
16947  * @param {object} config an object containing configurable attributes
16948  *                Valid properties for DD:
16949  *                    scroll
16950  */
16951 Roo.dd.DD = function(id, sGroup, config) {
16952     if (id) {
16953         this.init(id, sGroup, config);
16954     }
16955 };
16956
16957 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16958
16959     /**
16960      * When set to true, the utility automatically tries to scroll the browser
16961      * window wehn a drag and drop element is dragged near the viewport boundary.
16962      * Defaults to true.
16963      * @property scroll
16964      * @type boolean
16965      */
16966     scroll: true,
16967
16968     /**
16969      * Sets the pointer offset to the distance between the linked element's top
16970      * left corner and the location the element was clicked
16971      * @method autoOffset
16972      * @param {int} iPageX the X coordinate of the click
16973      * @param {int} iPageY the Y coordinate of the click
16974      */
16975     autoOffset: function(iPageX, iPageY) {
16976         var x = iPageX - this.startPageX;
16977         var y = iPageY - this.startPageY;
16978         this.setDelta(x, y);
16979     },
16980
16981     /**
16982      * Sets the pointer offset.  You can call this directly to force the
16983      * offset to be in a particular location (e.g., pass in 0,0 to set it
16984      * to the center of the object)
16985      * @method setDelta
16986      * @param {int} iDeltaX the distance from the left
16987      * @param {int} iDeltaY the distance from the top
16988      */
16989     setDelta: function(iDeltaX, iDeltaY) {
16990         this.deltaX = iDeltaX;
16991         this.deltaY = iDeltaY;
16992     },
16993
16994     /**
16995      * Sets the drag element to the location of the mousedown or click event,
16996      * maintaining the cursor location relative to the location on the element
16997      * that was clicked.  Override this if you want to place the element in a
16998      * location other than where the cursor is.
16999      * @method setDragElPos
17000      * @param {int} iPageX the X coordinate of the mousedown or drag event
17001      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17002      */
17003     setDragElPos: function(iPageX, iPageY) {
17004         // the first time we do this, we are going to check to make sure
17005         // the element has css positioning
17006
17007         var el = this.getDragEl();
17008         this.alignElWithMouse(el, iPageX, iPageY);
17009     },
17010
17011     /**
17012      * Sets the element to the location of the mousedown or click event,
17013      * maintaining the cursor location relative to the location on the element
17014      * that was clicked.  Override this if you want to place the element in a
17015      * location other than where the cursor is.
17016      * @method alignElWithMouse
17017      * @param {HTMLElement} el the element to move
17018      * @param {int} iPageX the X coordinate of the mousedown or drag event
17019      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17020      */
17021     alignElWithMouse: function(el, iPageX, iPageY) {
17022         var oCoord = this.getTargetCoord(iPageX, iPageY);
17023         var fly = el.dom ? el : Roo.fly(el);
17024         if (!this.deltaSetXY) {
17025             var aCoord = [oCoord.x, oCoord.y];
17026             fly.setXY(aCoord);
17027             var newLeft = fly.getLeft(true);
17028             var newTop  = fly.getTop(true);
17029             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17030         } else {
17031             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17032         }
17033
17034         this.cachePosition(oCoord.x, oCoord.y);
17035         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17036         return oCoord;
17037     },
17038
17039     /**
17040      * Saves the most recent position so that we can reset the constraints and
17041      * tick marks on-demand.  We need to know this so that we can calculate the
17042      * number of pixels the element is offset from its original position.
17043      * @method cachePosition
17044      * @param iPageX the current x position (optional, this just makes it so we
17045      * don't have to look it up again)
17046      * @param iPageY the current y position (optional, this just makes it so we
17047      * don't have to look it up again)
17048      */
17049     cachePosition: function(iPageX, iPageY) {
17050         if (iPageX) {
17051             this.lastPageX = iPageX;
17052             this.lastPageY = iPageY;
17053         } else {
17054             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17055             this.lastPageX = aCoord[0];
17056             this.lastPageY = aCoord[1];
17057         }
17058     },
17059
17060     /**
17061      * Auto-scroll the window if the dragged object has been moved beyond the
17062      * visible window boundary.
17063      * @method autoScroll
17064      * @param {int} x the drag element's x position
17065      * @param {int} y the drag element's y position
17066      * @param {int} h the height of the drag element
17067      * @param {int} w the width of the drag element
17068      * @private
17069      */
17070     autoScroll: function(x, y, h, w) {
17071
17072         if (this.scroll) {
17073             // The client height
17074             var clientH = Roo.lib.Dom.getViewWidth();
17075
17076             // The client width
17077             var clientW = Roo.lib.Dom.getViewHeight();
17078
17079             // The amt scrolled down
17080             var st = this.DDM.getScrollTop();
17081
17082             // The amt scrolled right
17083             var sl = this.DDM.getScrollLeft();
17084
17085             // Location of the bottom of the element
17086             var bot = h + y;
17087
17088             // Location of the right of the element
17089             var right = w + x;
17090
17091             // The distance from the cursor to the bottom of the visible area,
17092             // adjusted so that we don't scroll if the cursor is beyond the
17093             // element drag constraints
17094             var toBot = (clientH + st - y - this.deltaY);
17095
17096             // The distance from the cursor to the right of the visible area
17097             var toRight = (clientW + sl - x - this.deltaX);
17098
17099
17100             // How close to the edge the cursor must be before we scroll
17101             // var thresh = (document.all) ? 100 : 40;
17102             var thresh = 40;
17103
17104             // How many pixels to scroll per autoscroll op.  This helps to reduce
17105             // clunky scrolling. IE is more sensitive about this ... it needs this
17106             // value to be higher.
17107             var scrAmt = (document.all) ? 80 : 30;
17108
17109             // Scroll down if we are near the bottom of the visible page and the
17110             // obj extends below the crease
17111             if ( bot > clientH && toBot < thresh ) {
17112                 window.scrollTo(sl, st + scrAmt);
17113             }
17114
17115             // Scroll up if the window is scrolled down and the top of the object
17116             // goes above the top border
17117             if ( y < st && st > 0 && y - st < thresh ) {
17118                 window.scrollTo(sl, st - scrAmt);
17119             }
17120
17121             // Scroll right if the obj is beyond the right border and the cursor is
17122             // near the border.
17123             if ( right > clientW && toRight < thresh ) {
17124                 window.scrollTo(sl + scrAmt, st);
17125             }
17126
17127             // Scroll left if the window has been scrolled to the right and the obj
17128             // extends past the left border
17129             if ( x < sl && sl > 0 && x - sl < thresh ) {
17130                 window.scrollTo(sl - scrAmt, st);
17131             }
17132         }
17133     },
17134
17135     /**
17136      * Finds the location the element should be placed if we want to move
17137      * it to where the mouse location less the click offset would place us.
17138      * @method getTargetCoord
17139      * @param {int} iPageX the X coordinate of the click
17140      * @param {int} iPageY the Y coordinate of the click
17141      * @return an object that contains the coordinates (Object.x and Object.y)
17142      * @private
17143      */
17144     getTargetCoord: function(iPageX, iPageY) {
17145
17146
17147         var x = iPageX - this.deltaX;
17148         var y = iPageY - this.deltaY;
17149
17150         if (this.constrainX) {
17151             if (x < this.minX) { x = this.minX; }
17152             if (x > this.maxX) { x = this.maxX; }
17153         }
17154
17155         if (this.constrainY) {
17156             if (y < this.minY) { y = this.minY; }
17157             if (y > this.maxY) { y = this.maxY; }
17158         }
17159
17160         x = this.getTick(x, this.xTicks);
17161         y = this.getTick(y, this.yTicks);
17162
17163
17164         return {x:x, y:y};
17165     },
17166
17167     /*
17168      * Sets up config options specific to this class. Overrides
17169      * Roo.dd.DragDrop, but all versions of this method through the
17170      * inheritance chain are called
17171      */
17172     applyConfig: function() {
17173         Roo.dd.DD.superclass.applyConfig.call(this);
17174         this.scroll = (this.config.scroll !== false);
17175     },
17176
17177     /*
17178      * Event that fires prior to the onMouseDown event.  Overrides
17179      * Roo.dd.DragDrop.
17180      */
17181     b4MouseDown: function(e) {
17182         // this.resetConstraints();
17183         this.autoOffset(e.getPageX(),
17184                             e.getPageY());
17185     },
17186
17187     /*
17188      * Event that fires prior to the onDrag event.  Overrides
17189      * Roo.dd.DragDrop.
17190      */
17191     b4Drag: function(e) {
17192         this.setDragElPos(e.getPageX(),
17193                             e.getPageY());
17194     },
17195
17196     toString: function() {
17197         return ("DD " + this.id);
17198     }
17199
17200     //////////////////////////////////////////////////////////////////////////
17201     // Debugging ygDragDrop events that can be overridden
17202     //////////////////////////////////////////////////////////////////////////
17203     /*
17204     startDrag: function(x, y) {
17205     },
17206
17207     onDrag: function(e) {
17208     },
17209
17210     onDragEnter: function(e, id) {
17211     },
17212
17213     onDragOver: function(e, id) {
17214     },
17215
17216     onDragOut: function(e, id) {
17217     },
17218
17219     onDragDrop: function(e, id) {
17220     },
17221
17222     endDrag: function(e) {
17223     }
17224
17225     */
17226
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237
17238 /**
17239  * @class Roo.dd.DDProxy
17240  * A DragDrop implementation that inserts an empty, bordered div into
17241  * the document that follows the cursor during drag operations.  At the time of
17242  * the click, the frame div is resized to the dimensions of the linked html
17243  * element, and moved to the exact location of the linked element.
17244  *
17245  * References to the "frame" element refer to the single proxy element that
17246  * was created to be dragged in place of all DDProxy elements on the
17247  * page.
17248  *
17249  * @extends Roo.dd.DD
17250  * @constructor
17251  * @param {String} id the id of the linked html element
17252  * @param {String} sGroup the group of related DragDrop objects
17253  * @param {object} config an object containing configurable attributes
17254  *                Valid properties for DDProxy in addition to those in DragDrop:
17255  *                   resizeFrame, centerFrame, dragElId
17256  */
17257 Roo.dd.DDProxy = function(id, sGroup, config) {
17258     if (id) {
17259         this.init(id, sGroup, config);
17260         this.initFrame();
17261     }
17262 };
17263
17264 /**
17265  * The default drag frame div id
17266  * @property Roo.dd.DDProxy.dragElId
17267  * @type String
17268  * @static
17269  */
17270 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17271
17272 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17273
17274     /**
17275      * By default we resize the drag frame to be the same size as the element
17276      * we want to drag (this is to get the frame effect).  We can turn it off
17277      * if we want a different behavior.
17278      * @property resizeFrame
17279      * @type boolean
17280      */
17281     resizeFrame: true,
17282
17283     /**
17284      * By default the frame is positioned exactly where the drag element is, so
17285      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17286      * you do not have constraints on the obj is to have the drag frame centered
17287      * around the cursor.  Set centerFrame to true for this effect.
17288      * @property centerFrame
17289      * @type boolean
17290      */
17291     centerFrame: false,
17292
17293     /**
17294      * Creates the proxy element if it does not yet exist
17295      * @method createFrame
17296      */
17297     createFrame: function() {
17298         var self = this;
17299         var body = document.body;
17300
17301         if (!body || !body.firstChild) {
17302             setTimeout( function() { self.createFrame(); }, 50 );
17303             return;
17304         }
17305
17306         var div = this.getDragEl();
17307
17308         if (!div) {
17309             div    = document.createElement("div");
17310             div.id = this.dragElId;
17311             var s  = div.style;
17312
17313             s.position   = "absolute";
17314             s.visibility = "hidden";
17315             s.cursor     = "move";
17316             s.border     = "2px solid #aaa";
17317             s.zIndex     = 999;
17318
17319             // appendChild can blow up IE if invoked prior to the window load event
17320             // while rendering a table.  It is possible there are other scenarios
17321             // that would cause this to happen as well.
17322             body.insertBefore(div, body.firstChild);
17323         }
17324     },
17325
17326     /**
17327      * Initialization for the drag frame element.  Must be called in the
17328      * constructor of all subclasses
17329      * @method initFrame
17330      */
17331     initFrame: function() {
17332         this.createFrame();
17333     },
17334
17335     applyConfig: function() {
17336         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17337
17338         this.resizeFrame = (this.config.resizeFrame !== false);
17339         this.centerFrame = (this.config.centerFrame);
17340         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17341     },
17342
17343     /**
17344      * Resizes the drag frame to the dimensions of the clicked object, positions
17345      * it over the object, and finally displays it
17346      * @method showFrame
17347      * @param {int} iPageX X click position
17348      * @param {int} iPageY Y click position
17349      * @private
17350      */
17351     showFrame: function(iPageX, iPageY) {
17352         var el = this.getEl();
17353         var dragEl = this.getDragEl();
17354         var s = dragEl.style;
17355
17356         this._resizeProxy();
17357
17358         if (this.centerFrame) {
17359             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17360                            Math.round(parseInt(s.height, 10)/2) );
17361         }
17362
17363         this.setDragElPos(iPageX, iPageY);
17364
17365         Roo.fly(dragEl).show();
17366     },
17367
17368     /**
17369      * The proxy is automatically resized to the dimensions of the linked
17370      * element when a drag is initiated, unless resizeFrame is set to false
17371      * @method _resizeProxy
17372      * @private
17373      */
17374     _resizeProxy: function() {
17375         if (this.resizeFrame) {
17376             var el = this.getEl();
17377             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17378         }
17379     },
17380
17381     // overrides Roo.dd.DragDrop
17382     b4MouseDown: function(e) {
17383         var x = e.getPageX();
17384         var y = e.getPageY();
17385         this.autoOffset(x, y);
17386         this.setDragElPos(x, y);
17387     },
17388
17389     // overrides Roo.dd.DragDrop
17390     b4StartDrag: function(x, y) {
17391         // show the drag frame
17392         this.showFrame(x, y);
17393     },
17394
17395     // overrides Roo.dd.DragDrop
17396     b4EndDrag: function(e) {
17397         Roo.fly(this.getDragEl()).hide();
17398     },
17399
17400     // overrides Roo.dd.DragDrop
17401     // By default we try to move the element to the last location of the frame.
17402     // This is so that the default behavior mirrors that of Roo.dd.DD.
17403     endDrag: function(e) {
17404
17405         var lel = this.getEl();
17406         var del = this.getDragEl();
17407
17408         // Show the drag frame briefly so we can get its position
17409         del.style.visibility = "";
17410
17411         this.beforeMove();
17412         // Hide the linked element before the move to get around a Safari
17413         // rendering bug.
17414         lel.style.visibility = "hidden";
17415         Roo.dd.DDM.moveToEl(lel, del);
17416         del.style.visibility = "hidden";
17417         lel.style.visibility = "";
17418
17419         this.afterDrag();
17420     },
17421
17422     beforeMove : function(){
17423
17424     },
17425
17426     afterDrag : function(){
17427
17428     },
17429
17430     toString: function() {
17431         return ("DDProxy " + this.id);
17432     }
17433
17434 });
17435 /*
17436  * Based on:
17437  * Ext JS Library 1.1.1
17438  * Copyright(c) 2006-2007, Ext JS, LLC.
17439  *
17440  * Originally Released Under LGPL - original licence link has changed is not relivant.
17441  *
17442  * Fork - LGPL
17443  * <script type="text/javascript">
17444  */
17445
17446  /**
17447  * @class Roo.dd.DDTarget
17448  * A DragDrop implementation that does not move, but can be a drop
17449  * target.  You would get the same result by simply omitting implementation
17450  * for the event callbacks, but this way we reduce the processing cost of the
17451  * event listener and the callbacks.
17452  * @extends Roo.dd.DragDrop
17453  * @constructor
17454  * @param {String} id the id of the element that is a drop target
17455  * @param {String} sGroup the group of related DragDrop objects
17456  * @param {object} config an object containing configurable attributes
17457  *                 Valid properties for DDTarget in addition to those in
17458  *                 DragDrop:
17459  *                    none
17460  */
17461 Roo.dd.DDTarget = function(id, sGroup, config) {
17462     if (id) {
17463         this.initTarget(id, sGroup, config);
17464     }
17465     if (config.listeners || config.events) { 
17466        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17467             listeners : config.listeners || {}, 
17468             events : config.events || {} 
17469         });    
17470     }
17471 };
17472
17473 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17474 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17475     toString: function() {
17476         return ("DDTarget " + this.id);
17477     }
17478 });
17479 /*
17480  * Based on:
17481  * Ext JS Library 1.1.1
17482  * Copyright(c) 2006-2007, Ext JS, LLC.
17483  *
17484  * Originally Released Under LGPL - original licence link has changed is not relivant.
17485  *
17486  * Fork - LGPL
17487  * <script type="text/javascript">
17488  */
17489  
17490
17491 /**
17492  * @class Roo.dd.ScrollManager
17493  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17494  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17495  * @singleton
17496  */
17497 Roo.dd.ScrollManager = function(){
17498     var ddm = Roo.dd.DragDropMgr;
17499     var els = {};
17500     var dragEl = null;
17501     var proc = {};
17502     
17503     var onStop = function(e){
17504         dragEl = null;
17505         clearProc();
17506     };
17507     
17508     var triggerRefresh = function(){
17509         if(ddm.dragCurrent){
17510              ddm.refreshCache(ddm.dragCurrent.groups);
17511         }
17512     };
17513     
17514     var doScroll = function(){
17515         if(ddm.dragCurrent){
17516             var dds = Roo.dd.ScrollManager;
17517             if(!dds.animate){
17518                 if(proc.el.scroll(proc.dir, dds.increment)){
17519                     triggerRefresh();
17520                 }
17521             }else{
17522                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17523             }
17524         }
17525     };
17526     
17527     var clearProc = function(){
17528         if(proc.id){
17529             clearInterval(proc.id);
17530         }
17531         proc.id = 0;
17532         proc.el = null;
17533         proc.dir = "";
17534     };
17535     
17536     var startProc = function(el, dir){
17537         clearProc();
17538         proc.el = el;
17539         proc.dir = dir;
17540         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17541     };
17542     
17543     var onFire = function(e, isDrop){
17544         if(isDrop || !ddm.dragCurrent){ return; }
17545         var dds = Roo.dd.ScrollManager;
17546         if(!dragEl || dragEl != ddm.dragCurrent){
17547             dragEl = ddm.dragCurrent;
17548             // refresh regions on drag start
17549             dds.refreshCache();
17550         }
17551         
17552         var xy = Roo.lib.Event.getXY(e);
17553         var pt = new Roo.lib.Point(xy[0], xy[1]);
17554         for(var id in els){
17555             var el = els[id], r = el._region;
17556             if(r && r.contains(pt) && el.isScrollable()){
17557                 if(r.bottom - pt.y <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "down");
17560                     }
17561                     return;
17562                 }else if(r.right - pt.x <= dds.thresh){
17563                     if(proc.el != el){
17564                         startProc(el, "left");
17565                     }
17566                     return;
17567                 }else if(pt.y - r.top <= dds.thresh){
17568                     if(proc.el != el){
17569                         startProc(el, "up");
17570                     }
17571                     return;
17572                 }else if(pt.x - r.left <= dds.thresh){
17573                     if(proc.el != el){
17574                         startProc(el, "right");
17575                     }
17576                     return;
17577                 }
17578             }
17579         }
17580         clearProc();
17581     };
17582     
17583     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17584     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17585     
17586     return {
17587         /**
17588          * Registers new overflow element(s) to auto scroll
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17590          */
17591         register : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.register(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 els[el.id] = el;
17599             }
17600         },
17601         
17602         /**
17603          * Unregisters overflow element(s) so they are no longer scrolled
17604          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17605          */
17606         unregister : function(el){
17607             if(el instanceof Array){
17608                 for(var i = 0, len = el.length; i < len; i++) {
17609                         this.unregister(el[i]);
17610                 }
17611             }else{
17612                 el = Roo.get(el);
17613                 delete els[el.id];
17614             }
17615         },
17616         
17617         /**
17618          * The number of pixels from the edge of a container the pointer needs to be to 
17619          * trigger scrolling (defaults to 25)
17620          * @type Number
17621          */
17622         thresh : 25,
17623         
17624         /**
17625          * The number of pixels to scroll in each scroll increment (defaults to 50)
17626          * @type Number
17627          */
17628         increment : 100,
17629         
17630         /**
17631          * The frequency of scrolls in milliseconds (defaults to 500)
17632          * @type Number
17633          */
17634         frequency : 500,
17635         
17636         /**
17637          * True to animate the scroll (defaults to true)
17638          * @type Boolean
17639          */
17640         animate: true,
17641         
17642         /**
17643          * The animation duration in seconds - 
17644          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17645          * @type Number
17646          */
17647         animDuration: .4,
17648         
17649         /**
17650          * Manually trigger a cache refresh.
17651          */
17652         refreshCache : function(){
17653             for(var id in els){
17654                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17655                     els[id]._region = els[id].getRegion();
17656                 }
17657             }
17658         }
17659     };
17660 }();/*
17661  * Based on:
17662  * Ext JS Library 1.1.1
17663  * Copyright(c) 2006-2007, Ext JS, LLC.
17664  *
17665  * Originally Released Under LGPL - original licence link has changed is not relivant.
17666  *
17667  * Fork - LGPL
17668  * <script type="text/javascript">
17669  */
17670  
17671
17672 /**
17673  * @class Roo.dd.Registry
17674  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17675  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17676  * @singleton
17677  */
17678 Roo.dd.Registry = function(){
17679     var elements = {}; 
17680     var handles = {}; 
17681     var autoIdSeed = 0;
17682
17683     var getId = function(el, autogen){
17684         if(typeof el == "string"){
17685             return el;
17686         }
17687         var id = el.id;
17688         if(!id && autogen !== false){
17689             id = "roodd-" + (++autoIdSeed);
17690             el.id = id;
17691         }
17692         return id;
17693     };
17694     
17695     return {
17696     /**
17697      * Register a drag drop element
17698      * @param {String|HTMLElement} element The id or DOM node to register
17699      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17700      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17701      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17702      * populated in the data object (if applicable):
17703      * <pre>
17704 Value      Description<br />
17705 ---------  ------------------------------------------<br />
17706 handles    Array of DOM nodes that trigger dragging<br />
17707            for the element being registered<br />
17708 isHandle   True if the element passed in triggers<br />
17709            dragging itself, else false
17710 </pre>
17711      */
17712         register : function(el, data){
17713             data = data || {};
17714             if(typeof el == "string"){
17715                 el = document.getElementById(el);
17716             }
17717             data.ddel = el;
17718             elements[getId(el)] = data;
17719             if(data.isHandle !== false){
17720                 handles[data.ddel.id] = data;
17721             }
17722             if(data.handles){
17723                 var hs = data.handles;
17724                 for(var i = 0, len = hs.length; i < len; i++){
17725                         handles[getId(hs[i])] = data;
17726                 }
17727             }
17728         },
17729
17730     /**
17731      * Unregister a drag drop element
17732      * @param {String|HTMLElement}  element The id or DOM node to unregister
17733      */
17734         unregister : function(el){
17735             var id = getId(el, false);
17736             var data = elements[id];
17737             if(data){
17738                 delete elements[id];
17739                 if(data.handles){
17740                     var hs = data.handles;
17741                     for(var i = 0, len = hs.length; i < len; i++){
17742                         delete handles[getId(hs[i], false)];
17743                     }
17744                 }
17745             }
17746         },
17747
17748     /**
17749      * Returns the handle registered for a DOM Node by id
17750      * @param {String|HTMLElement} id The DOM node or id to look up
17751      * @return {Object} handle The custom handle data
17752      */
17753         getHandle : function(id){
17754             if(typeof id != "string"){ // must be element?
17755                 id = id.id;
17756             }
17757             return handles[id];
17758         },
17759
17760     /**
17761      * Returns the handle that is registered for the DOM node that is the target of the event
17762      * @param {Event} e The event
17763      * @return {Object} handle The custom handle data
17764      */
17765         getHandleFromEvent : function(e){
17766             var t = Roo.lib.Event.getTarget(e);
17767             return t ? handles[t.id] : null;
17768         },
17769
17770     /**
17771      * Returns a custom data object that is registered for a DOM node by id
17772      * @param {String|HTMLElement} id The DOM node or id to look up
17773      * @return {Object} data The custom data
17774      */
17775         getTarget : function(id){
17776             if(typeof id != "string"){ // must be element?
17777                 id = id.id;
17778             }
17779             return elements[id];
17780         },
17781
17782     /**
17783      * Returns a custom data object that is registered for the DOM node that is the target of the event
17784      * @param {Event} e The event
17785      * @return {Object} data The custom data
17786      */
17787         getTargetFromEvent : function(e){
17788             var t = Roo.lib.Event.getTarget(e);
17789             return t ? elements[t.id] || handles[t.id] : null;
17790         }
17791     };
17792 }();/*
17793  * Based on:
17794  * Ext JS Library 1.1.1
17795  * Copyright(c) 2006-2007, Ext JS, LLC.
17796  *
17797  * Originally Released Under LGPL - original licence link has changed is not relivant.
17798  *
17799  * Fork - LGPL
17800  * <script type="text/javascript">
17801  */
17802  
17803
17804 /**
17805  * @class Roo.dd.StatusProxy
17806  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17807  * default drag proxy used by all Roo.dd components.
17808  * @constructor
17809  * @param {Object} config
17810  */
17811 Roo.dd.StatusProxy = function(config){
17812     Roo.apply(this, config);
17813     this.id = this.id || Roo.id();
17814     this.el = new Roo.Layer({
17815         dh: {
17816             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17817                 {tag: "div", cls: "x-dd-drop-icon"},
17818                 {tag: "div", cls: "x-dd-drag-ghost"}
17819             ]
17820         }, 
17821         shadow: !config || config.shadow !== false
17822     });
17823     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17824     this.dropStatus = this.dropNotAllowed;
17825 };
17826
17827 Roo.dd.StatusProxy.prototype = {
17828     /**
17829      * @cfg {String} dropAllowed
17830      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17831      */
17832     dropAllowed : "x-dd-drop-ok",
17833     /**
17834      * @cfg {String} dropNotAllowed
17835      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17836      */
17837     dropNotAllowed : "x-dd-drop-nodrop",
17838
17839     /**
17840      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17841      * over the current target element.
17842      * @param {String} cssClass The css class for the new drop status indicator image
17843      */
17844     setStatus : function(cssClass){
17845         cssClass = cssClass || this.dropNotAllowed;
17846         if(this.dropStatus != cssClass){
17847             this.el.replaceClass(this.dropStatus, cssClass);
17848             this.dropStatus = cssClass;
17849         }
17850     },
17851
17852     /**
17853      * Resets the status indicator to the default dropNotAllowed value
17854      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17855      */
17856     reset : function(clearGhost){
17857         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17858         this.dropStatus = this.dropNotAllowed;
17859         if(clearGhost){
17860             this.ghost.update("");
17861         }
17862     },
17863
17864     /**
17865      * Updates the contents of the ghost element
17866      * @param {String} html The html that will replace the current innerHTML of the ghost element
17867      */
17868     update : function(html){
17869         if(typeof html == "string"){
17870             this.ghost.update(html);
17871         }else{
17872             this.ghost.update("");
17873             html.style.margin = "0";
17874             this.ghost.dom.appendChild(html);
17875         }
17876         // ensure float = none set?? cant remember why though.
17877         var el = this.ghost.dom.firstChild;
17878                 if(el){
17879                         Roo.fly(el).setStyle('float', 'none');
17880                 }
17881     },
17882     
17883     /**
17884      * Returns the underlying proxy {@link Roo.Layer}
17885      * @return {Roo.Layer} el
17886     */
17887     getEl : function(){
17888         return this.el;
17889     },
17890
17891     /**
17892      * Returns the ghost element
17893      * @return {Roo.Element} el
17894      */
17895     getGhost : function(){
17896         return this.ghost;
17897     },
17898
17899     /**
17900      * Hides the proxy
17901      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17902      */
17903     hide : function(clear){
17904         this.el.hide();
17905         if(clear){
17906             this.reset(true);
17907         }
17908     },
17909
17910     /**
17911      * Stops the repair animation if it's currently running
17912      */
17913     stop : function(){
17914         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17915             this.anim.stop();
17916         }
17917     },
17918
17919     /**
17920      * Displays this proxy
17921      */
17922     show : function(){
17923         this.el.show();
17924     },
17925
17926     /**
17927      * Force the Layer to sync its shadow and shim positions to the element
17928      */
17929     sync : function(){
17930         this.el.sync();
17931     },
17932
17933     /**
17934      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17935      * invalid drop operation by the item being dragged.
17936      * @param {Array} xy The XY position of the element ([x, y])
17937      * @param {Function} callback The function to call after the repair is complete
17938      * @param {Object} scope The scope in which to execute the callback
17939      */
17940     repair : function(xy, callback, scope){
17941         this.callback = callback;
17942         this.scope = scope;
17943         if(xy && this.animRepair !== false){
17944             this.el.addClass("x-dd-drag-repair");
17945             this.el.hideUnders(true);
17946             this.anim = this.el.shift({
17947                 duration: this.repairDuration || .5,
17948                 easing: 'easeOut',
17949                 xy: xy,
17950                 stopFx: true,
17951                 callback: this.afterRepair,
17952                 scope: this
17953             });
17954         }else{
17955             this.afterRepair();
17956         }
17957     },
17958
17959     // private
17960     afterRepair : function(){
17961         this.hide(true);
17962         if(typeof this.callback == "function"){
17963             this.callback.call(this.scope || this);
17964         }
17965         this.callback = null;
17966         this.scope = null;
17967     }
17968 };/*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978
17979 /**
17980  * @class Roo.dd.DragSource
17981  * @extends Roo.dd.DDProxy
17982  * A simple class that provides the basic implementation needed to make any element draggable.
17983  * @constructor
17984  * @param {String/HTMLElement/Element} el The container element
17985  * @param {Object} config
17986  */
17987 Roo.dd.DragSource = function(el, config){
17988     this.el = Roo.get(el);
17989     this.dragData = {};
17990     
17991     Roo.apply(this, config);
17992     
17993     if(!this.proxy){
17994         this.proxy = new Roo.dd.StatusProxy();
17995     }
17996
17997     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17998           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17999     
18000     this.dragging = false;
18001 };
18002
18003 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18004     /**
18005      * @cfg {String} dropAllowed
18006      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18007      */
18008     dropAllowed : "x-dd-drop-ok",
18009     /**
18010      * @cfg {String} dropNotAllowed
18011      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18012      */
18013     dropNotAllowed : "x-dd-drop-nodrop",
18014
18015     /**
18016      * Returns the data object associated with this drag source
18017      * @return {Object} data An object containing arbitrary data
18018      */
18019     getDragData : function(e){
18020         return this.dragData;
18021     },
18022
18023     // private
18024     onDragEnter : function(e, id){
18025         var target = Roo.dd.DragDropMgr.getDDById(id);
18026         this.cachedTarget = target;
18027         if(this.beforeDragEnter(target, e, id) !== false){
18028             if(target.isNotifyTarget){
18029                 var status = target.notifyEnter(this, e, this.dragData);
18030                 this.proxy.setStatus(status);
18031             }else{
18032                 this.proxy.setStatus(this.dropAllowed);
18033             }
18034             
18035             if(this.afterDragEnter){
18036                 /**
18037                  * An empty function by default, but provided so that you can perform a custom action
18038                  * when the dragged item enters the drop target by providing an implementation.
18039                  * @param {Roo.dd.DragDrop} target The drop target
18040                  * @param {Event} e The event object
18041                  * @param {String} id The id of the dragged element
18042                  * @method afterDragEnter
18043                  */
18044                 this.afterDragEnter(target, e, id);
18045             }
18046         }
18047     },
18048
18049     /**
18050      * An empty function by default, but provided so that you can perform a custom action
18051      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18052      * @param {Roo.dd.DragDrop} target The drop target
18053      * @param {Event} e The event object
18054      * @param {String} id The id of the dragged element
18055      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18056      */
18057     beforeDragEnter : function(target, e, id){
18058         return true;
18059     },
18060
18061     // private
18062     alignElWithMouse: function() {
18063         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18064         this.proxy.sync();
18065     },
18066
18067     // private
18068     onDragOver : function(e, id){
18069         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18070         if(this.beforeDragOver(target, e, id) !== false){
18071             if(target.isNotifyTarget){
18072                 var status = target.notifyOver(this, e, this.dragData);
18073                 this.proxy.setStatus(status);
18074             }
18075
18076             if(this.afterDragOver){
18077                 /**
18078                  * An empty function by default, but provided so that you can perform a custom action
18079                  * while the dragged item is over the drop target by providing an implementation.
18080                  * @param {Roo.dd.DragDrop} target The drop target
18081                  * @param {Event} e The event object
18082                  * @param {String} id The id of the dragged element
18083                  * @method afterDragOver
18084                  */
18085                 this.afterDragOver(target, e, id);
18086             }
18087         }
18088     },
18089
18090     /**
18091      * An empty function by default, but provided so that you can perform a custom action
18092      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18093      * @param {Roo.dd.DragDrop} target The drop target
18094      * @param {Event} e The event object
18095      * @param {String} id The id of the dragged element
18096      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18097      */
18098     beforeDragOver : function(target, e, id){
18099         return true;
18100     },
18101
18102     // private
18103     onDragOut : function(e, id){
18104         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18105         if(this.beforeDragOut(target, e, id) !== false){
18106             if(target.isNotifyTarget){
18107                 target.notifyOut(this, e, this.dragData);
18108             }
18109             this.proxy.reset();
18110             if(this.afterDragOut){
18111                 /**
18112                  * An empty function by default, but provided so that you can perform a custom action
18113                  * after the dragged item is dragged out of the target without dropping.
18114                  * @param {Roo.dd.DragDrop} target The drop target
18115                  * @param {Event} e The event object
18116                  * @param {String} id The id of the dragged element
18117                  * @method afterDragOut
18118                  */
18119                 this.afterDragOut(target, e, id);
18120             }
18121         }
18122         this.cachedTarget = null;
18123     },
18124
18125     /**
18126      * An empty function by default, but provided so that you can perform a custom action before the dragged
18127      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18128      * @param {Roo.dd.DragDrop} target The drop target
18129      * @param {Event} e The event object
18130      * @param {String} id The id of the dragged element
18131      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18132      */
18133     beforeDragOut : function(target, e, id){
18134         return true;
18135     },
18136     
18137     // private
18138     onDragDrop : function(e, id){
18139         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18140         if(this.beforeDragDrop(target, e, id) !== false){
18141             if(target.isNotifyTarget){
18142                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18143                     this.onValidDrop(target, e, id);
18144                 }else{
18145                     this.onInvalidDrop(target, e, id);
18146                 }
18147             }else{
18148                 this.onValidDrop(target, e, id);
18149             }
18150             
18151             if(this.afterDragDrop){
18152                 /**
18153                  * An empty function by default, but provided so that you can perform a custom action
18154                  * after a valid drag drop has occurred by providing an implementation.
18155                  * @param {Roo.dd.DragDrop} target The drop target
18156                  * @param {Event} e The event object
18157                  * @param {String} id The id of the dropped element
18158                  * @method afterDragDrop
18159                  */
18160                 this.afterDragDrop(target, e, id);
18161             }
18162         }
18163         delete this.cachedTarget;
18164     },
18165
18166     /**
18167      * An empty function by default, but provided so that you can perform a custom action before the dragged
18168      * item is dropped onto the target and optionally cancel the onDragDrop.
18169      * @param {Roo.dd.DragDrop} target The drop target
18170      * @param {Event} e The event object
18171      * @param {String} id The id of the dragged element
18172      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18173      */
18174     beforeDragDrop : function(target, e, id){
18175         return true;
18176     },
18177
18178     // private
18179     onValidDrop : function(target, e, id){
18180         this.hideProxy();
18181         if(this.afterValidDrop){
18182             /**
18183              * An empty function by default, but provided so that you can perform a custom action
18184              * after a valid drop has occurred by providing an implementation.
18185              * @param {Object} target The target DD 
18186              * @param {Event} e The event object
18187              * @param {String} id The id of the dropped element
18188              * @method afterInvalidDrop
18189              */
18190             this.afterValidDrop(target, e, id);
18191         }
18192     },
18193
18194     // private
18195     getRepairXY : function(e, data){
18196         return this.el.getXY();  
18197     },
18198
18199     // private
18200     onInvalidDrop : function(target, e, id){
18201         this.beforeInvalidDrop(target, e, id);
18202         if(this.cachedTarget){
18203             if(this.cachedTarget.isNotifyTarget){
18204                 this.cachedTarget.notifyOut(this, e, this.dragData);
18205             }
18206             this.cacheTarget = null;
18207         }
18208         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18209
18210         if(this.afterInvalidDrop){
18211             /**
18212              * An empty function by default, but provided so that you can perform a custom action
18213              * after an invalid drop has occurred by providing an implementation.
18214              * @param {Event} e The event object
18215              * @param {String} id The id of the dropped element
18216              * @method afterInvalidDrop
18217              */
18218             this.afterInvalidDrop(e, id);
18219         }
18220     },
18221
18222     // private
18223     afterRepair : function(){
18224         if(Roo.enableFx){
18225             this.el.highlight(this.hlColor || "c3daf9");
18226         }
18227         this.dragging = false;
18228     },
18229
18230     /**
18231      * An empty function by default, but provided so that you can perform a custom action after an invalid
18232      * drop has occurred.
18233      * @param {Roo.dd.DragDrop} target The drop target
18234      * @param {Event} e The event object
18235      * @param {String} id The id of the dragged element
18236      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18237      */
18238     beforeInvalidDrop : function(target, e, id){
18239         return true;
18240     },
18241
18242     // private
18243     handleMouseDown : function(e){
18244         if(this.dragging) {
18245             return;
18246         }
18247         var data = this.getDragData(e);
18248         if(data && this.onBeforeDrag(data, e) !== false){
18249             this.dragData = data;
18250             this.proxy.stop();
18251             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18252         } 
18253     },
18254
18255     /**
18256      * An empty function by default, but provided so that you can perform a custom action before the initial
18257      * drag event begins and optionally cancel it.
18258      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18259      * @param {Event} e The event object
18260      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18261      */
18262     onBeforeDrag : function(data, e){
18263         return true;
18264     },
18265
18266     /**
18267      * An empty function by default, but provided so that you can perform a custom action once the initial
18268      * drag event has begun.  The drag cannot be canceled from this function.
18269      * @param {Number} x The x position of the click on the dragged object
18270      * @param {Number} y The y position of the click on the dragged object
18271      */
18272     onStartDrag : Roo.emptyFn,
18273
18274     // private - YUI override
18275     startDrag : function(x, y){
18276         this.proxy.reset();
18277         this.dragging = true;
18278         this.proxy.update("");
18279         this.onInitDrag(x, y);
18280         this.proxy.show();
18281     },
18282
18283     // private
18284     onInitDrag : function(x, y){
18285         var clone = this.el.dom.cloneNode(true);
18286         clone.id = Roo.id(); // prevent duplicate ids
18287         this.proxy.update(clone);
18288         this.onStartDrag(x, y);
18289         return true;
18290     },
18291
18292     /**
18293      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18294      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18295      */
18296     getProxy : function(){
18297         return this.proxy;  
18298     },
18299
18300     /**
18301      * Hides the drag source's {@link Roo.dd.StatusProxy}
18302      */
18303     hideProxy : function(){
18304         this.proxy.hide();  
18305         this.proxy.reset(true);
18306         this.dragging = false;
18307     },
18308
18309     // private
18310     triggerCacheRefresh : function(){
18311         Roo.dd.DDM.refreshCache(this.groups);
18312     },
18313
18314     // private - override to prevent hiding
18315     b4EndDrag: function(e) {
18316     },
18317
18318     // private - override to prevent moving
18319     endDrag : function(e){
18320         this.onEndDrag(this.dragData, e);
18321     },
18322
18323     // private
18324     onEndDrag : function(data, e){
18325     },
18326     
18327     // private - pin to cursor
18328     autoOffset : function(x, y) {
18329         this.setDelta(-12, -20);
18330     }    
18331 });/*
18332  * Based on:
18333  * Ext JS Library 1.1.1
18334  * Copyright(c) 2006-2007, Ext JS, LLC.
18335  *
18336  * Originally Released Under LGPL - original licence link has changed is not relivant.
18337  *
18338  * Fork - LGPL
18339  * <script type="text/javascript">
18340  */
18341
18342
18343 /**
18344  * @class Roo.dd.DropTarget
18345  * @extends Roo.dd.DDTarget
18346  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18347  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18348  * @constructor
18349  * @param {String/HTMLElement/Element} el The container element
18350  * @param {Object} config
18351  */
18352 Roo.dd.DropTarget = function(el, config){
18353     this.el = Roo.get(el);
18354     
18355     var listeners = false; ;
18356     if (config && config.listeners) {
18357         listeners= config.listeners;
18358         delete config.listeners;
18359     }
18360     Roo.apply(this, config);
18361     
18362     if(this.containerScroll){
18363         Roo.dd.ScrollManager.register(this.el);
18364     }
18365     this.addEvents( {
18366          /**
18367          * @scope Roo.dd.DropTarget
18368          */
18369          
18370          /**
18371          * @event enter
18372          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18373          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18374          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18375          * 
18376          * IMPORTANT : it should set this.overClass and this.dropAllowed
18377          * 
18378          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18379          * @param {Event} e The event
18380          * @param {Object} data An object containing arbitrary data supplied by the drag source
18381          */
18382         "enter" : true,
18383         
18384          /**
18385          * @event over
18386          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18387          * This method will be called on every mouse movement while the drag source is over the drop target.
18388          * This default implementation simply returns the dropAllowed config value.
18389          * 
18390          * IMPORTANT : it should set this.dropAllowed
18391          * 
18392          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18393          * @param {Event} e The event
18394          * @param {Object} data An object containing arbitrary data supplied by the drag source
18395          
18396          */
18397         "over" : true,
18398         /**
18399          * @event out
18400          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18401          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18402          * overClass (if any) from the drop element.
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406          */
18407          "out" : true,
18408          
18409         /**
18410          * @event drop
18411          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18412          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18413          * implementation that does something to process the drop event and returns true so that the drag source's
18414          * repair action does not run.
18415          * 
18416          * IMPORTANT : it should set this.success
18417          * 
18418          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18419          * @param {Event} e The event
18420          * @param {Object} data An object containing arbitrary data supplied by the drag source
18421         */
18422          "drop" : true
18423     });
18424             
18425      
18426     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18427         this.el.dom, 
18428         this.ddGroup || this.group,
18429         {
18430             isTarget: true,
18431             listeners : listeners || {} 
18432            
18433         
18434         }
18435     );
18436
18437 };
18438
18439 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18440     /**
18441      * @cfg {String} overClass
18442      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18443      */
18444      /**
18445      * @cfg {String} ddGroup
18446      * The drag drop group to handle drop events for
18447      */
18448      
18449     /**
18450      * @cfg {String} dropAllowed
18451      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18452      */
18453     dropAllowed : "x-dd-drop-ok",
18454     /**
18455      * @cfg {String} dropNotAllowed
18456      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18457      */
18458     dropNotAllowed : "x-dd-drop-nodrop",
18459     /**
18460      * @cfg {boolean} success
18461      * set this after drop listener.. 
18462      */
18463     success : false,
18464     /**
18465      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18466      * if the drop point is valid for over/enter..
18467      */
18468     valid : false,
18469     // private
18470     isTarget : true,
18471
18472     // private
18473     isNotifyTarget : true,
18474     
18475     /**
18476      * @hide
18477      */
18478     notifyEnter : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('enter', dd, e, data);
18482         if(this.overClass){
18483             this.el.addClass(this.overClass);
18484         }
18485         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18486             this.valid ? this.dropAllowed : this.dropNotAllowed
18487         );
18488     },
18489
18490     /**
18491      * @hide
18492      */
18493     notifyOver : function(dd, e, data)
18494     {
18495         this.valid = true;
18496         this.fireEvent('over', dd, e, data);
18497         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18498             this.valid ? this.dropAllowed : this.dropNotAllowed
18499         );
18500     },
18501
18502     /**
18503      * @hide
18504      */
18505     notifyOut : function(dd, e, data)
18506     {
18507         this.fireEvent('out', dd, e, data);
18508         if(this.overClass){
18509             this.el.removeClass(this.overClass);
18510         }
18511     },
18512
18513     /**
18514      * @hide
18515      */
18516     notifyDrop : function(dd, e, data)
18517     {
18518         this.success = false;
18519         this.fireEvent('drop', dd, e, data);
18520         return this.success;
18521     }
18522 });/*
18523  * Based on:
18524  * Ext JS Library 1.1.1
18525  * Copyright(c) 2006-2007, Ext JS, LLC.
18526  *
18527  * Originally Released Under LGPL - original licence link has changed is not relivant.
18528  *
18529  * Fork - LGPL
18530  * <script type="text/javascript">
18531  */
18532
18533
18534 /**
18535  * @class Roo.dd.DragZone
18536  * @extends Roo.dd.DragSource
18537  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18538  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18539  * @constructor
18540  * @param {String/HTMLElement/Element} el The container element
18541  * @param {Object} config
18542  */
18543 Roo.dd.DragZone = function(el, config){
18544     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18545     if(this.containerScroll){
18546         Roo.dd.ScrollManager.register(this.el);
18547     }
18548 };
18549
18550 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18551     /**
18552      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18553      * for auto scrolling during drag operations.
18554      */
18555     /**
18556      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18557      * method after a failed drop (defaults to "c3daf9" - light blue)
18558      */
18559
18560     /**
18561      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18562      * for a valid target to drag based on the mouse down. Override this method
18563      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18564      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18565      * @param {EventObject} e The mouse down event
18566      * @return {Object} The dragData
18567      */
18568     getDragData : function(e){
18569         return Roo.dd.Registry.getHandleFromEvent(e);
18570     },
18571     
18572     /**
18573      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18574      * this.dragData.ddel
18575      * @param {Number} x The x position of the click on the dragged object
18576      * @param {Number} y The y position of the click on the dragged object
18577      * @return {Boolean} true to continue the drag, false to cancel
18578      */
18579     onInitDrag : function(x, y){
18580         this.proxy.update(this.dragData.ddel.cloneNode(true));
18581         this.onStartDrag(x, y);
18582         return true;
18583     },
18584     
18585     /**
18586      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18587      */
18588     afterRepair : function(){
18589         if(Roo.enableFx){
18590             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18591         }
18592         this.dragging = false;
18593     },
18594
18595     /**
18596      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18597      * the XY of this.dragData.ddel
18598      * @param {EventObject} e The mouse up event
18599      * @return {Array} The xy location (e.g. [100, 200])
18600      */
18601     getRepairXY : function(e){
18602         return Roo.Element.fly(this.dragData.ddel).getXY();  
18603     }
18604 });/*
18605  * Based on:
18606  * Ext JS Library 1.1.1
18607  * Copyright(c) 2006-2007, Ext JS, LLC.
18608  *
18609  * Originally Released Under LGPL - original licence link has changed is not relivant.
18610  *
18611  * Fork - LGPL
18612  * <script type="text/javascript">
18613  */
18614 /**
18615  * @class Roo.dd.DropZone
18616  * @extends Roo.dd.DropTarget
18617  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18618  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18619  * @constructor
18620  * @param {String/HTMLElement/Element} el The container element
18621  * @param {Object} config
18622  */
18623 Roo.dd.DropZone = function(el, config){
18624     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18625 };
18626
18627 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18628     /**
18629      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18630      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18631      * provide your own custom lookup.
18632      * @param {Event} e The event
18633      * @return {Object} data The custom data
18634      */
18635     getTargetFromEvent : function(e){
18636         return Roo.dd.Registry.getTargetFromEvent(e);
18637     },
18638
18639     /**
18640      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18641      * that it has registered.  This method has no default implementation and should be overridden to provide
18642      * node-specific processing if necessary.
18643      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18644      * {@link #getTargetFromEvent} for this node)
18645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18646      * @param {Event} e The event
18647      * @param {Object} data An object containing arbitrary data supplied by the drag source
18648      */
18649     onNodeEnter : function(n, dd, e, data){
18650         
18651     },
18652
18653     /**
18654      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18655      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18656      * overridden to provide the proper feedback.
18657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18658      * {@link #getTargetFromEvent} for this node)
18659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18660      * @param {Event} e The event
18661      * @param {Object} data An object containing arbitrary data supplied by the drag source
18662      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18663      * underlying {@link Roo.dd.StatusProxy} can be updated
18664      */
18665     onNodeOver : function(n, dd, e, data){
18666         return this.dropAllowed;
18667     },
18668
18669     /**
18670      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18671      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18672      * node-specific processing if necessary.
18673      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18674      * {@link #getTargetFromEvent} for this node)
18675      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18676      * @param {Event} e The event
18677      * @param {Object} data An object containing arbitrary data supplied by the drag source
18678      */
18679     onNodeOut : function(n, dd, e, data){
18680         
18681     },
18682
18683     /**
18684      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18685      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18686      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18688      * {@link #getTargetFromEvent} for this node)
18689      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18690      * @param {Event} e The event
18691      * @param {Object} data An object containing arbitrary data supplied by the drag source
18692      * @return {Boolean} True if the drop was valid, else false
18693      */
18694     onNodeDrop : function(n, dd, e, data){
18695         return false;
18696     },
18697
18698     /**
18699      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18700      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18701      * it should be overridden to provide the proper feedback if necessary.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18706      * underlying {@link Roo.dd.StatusProxy} can be updated
18707      */
18708     onContainerOver : function(dd, e, data){
18709         return this.dropNotAllowed;
18710     },
18711
18712     /**
18713      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18714      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18715      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18716      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      * @return {Boolean} True if the drop was valid, else false
18721      */
18722     onContainerDrop : function(dd, e, data){
18723         return false;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18728      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18729      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18730      * you should override this method and provide a custom implementation.
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18735      * underlying {@link Roo.dd.StatusProxy} can be updated
18736      */
18737     notifyEnter : function(dd, e, data){
18738         return this.dropNotAllowed;
18739     },
18740
18741     /**
18742      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18743      * This method will be called on every mouse movement while the drag source is over the drop zone.
18744      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18745      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18746      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18747      * registered node, it will call {@link #onContainerOver}.
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18752      * underlying {@link Roo.dd.StatusProxy} can be updated
18753      */
18754     notifyOver : function(dd, e, data){
18755         var n = this.getTargetFromEvent(e);
18756         if(!n){ // not over valid drop target
18757             if(this.lastOverNode){
18758                 this.onNodeOut(this.lastOverNode, dd, e, data);
18759                 this.lastOverNode = null;
18760             }
18761             return this.onContainerOver(dd, e, data);
18762         }
18763         if(this.lastOverNode != n){
18764             if(this.lastOverNode){
18765                 this.onNodeOut(this.lastOverNode, dd, e, data);
18766             }
18767             this.onNodeEnter(n, dd, e, data);
18768             this.lastOverNode = n;
18769         }
18770         return this.onNodeOver(n, dd, e, data);
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18775      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18776      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18778      * @param {Event} e The event
18779      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18780      */
18781     notifyOut : function(dd, e, data){
18782         if(this.lastOverNode){
18783             this.onNodeOut(this.lastOverNode, dd, e, data);
18784             this.lastOverNode = null;
18785         }
18786     },
18787
18788     /**
18789      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18790      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18791      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18792      * otherwise it will call {@link #onContainerDrop}.
18793      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18794      * @param {Event} e The event
18795      * @param {Object} data An object containing arbitrary data supplied by the drag source
18796      * @return {Boolean} True if the drop was valid, else false
18797      */
18798     notifyDrop : function(dd, e, data){
18799         if(this.lastOverNode){
18800             this.onNodeOut(this.lastOverNode, dd, e, data);
18801             this.lastOverNode = null;
18802         }
18803         var n = this.getTargetFromEvent(e);
18804         return n ?
18805             this.onNodeDrop(n, dd, e, data) :
18806             this.onContainerDrop(dd, e, data);
18807     },
18808
18809     // private
18810     triggerCacheRefresh : function(){
18811         Roo.dd.DDM.refreshCache(this.groups);
18812     }  
18813 });/*
18814  * Based on:
18815  * Ext JS Library 1.1.1
18816  * Copyright(c) 2006-2007, Ext JS, LLC.
18817  *
18818  * Originally Released Under LGPL - original licence link has changed is not relivant.
18819  *
18820  * Fork - LGPL
18821  * <script type="text/javascript">
18822  */
18823
18824
18825 /**
18826  * @class Roo.data.SortTypes
18827  * @singleton
18828  * Defines the default sorting (casting?) comparison functions used when sorting data.
18829  */
18830 Roo.data.SortTypes = {
18831     /**
18832      * Default sort that does nothing
18833      * @param {Mixed} s The value being converted
18834      * @return {Mixed} The comparison value
18835      */
18836     none : function(s){
18837         return s;
18838     },
18839     
18840     /**
18841      * The regular expression used to strip tags
18842      * @type {RegExp}
18843      * @property
18844      */
18845     stripTagsRE : /<\/?[^>]+>/gi,
18846     
18847     /**
18848      * Strips all HTML tags to sort on text only
18849      * @param {Mixed} s The value being converted
18850      * @return {String} The comparison value
18851      */
18852     asText : function(s){
18853         return String(s).replace(this.stripTagsRE, "");
18854     },
18855     
18856     /**
18857      * Strips all HTML tags to sort on text only - Case insensitive
18858      * @param {Mixed} s The value being converted
18859      * @return {String} The comparison value
18860      */
18861     asUCText : function(s){
18862         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18863     },
18864     
18865     /**
18866      * Case insensitive string
18867      * @param {Mixed} s The value being converted
18868      * @return {String} The comparison value
18869      */
18870     asUCString : function(s) {
18871         return String(s).toUpperCase();
18872     },
18873     
18874     /**
18875      * Date sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Number} The comparison value
18878      */
18879     asDate : function(s) {
18880         if(!s){
18881             return 0;
18882         }
18883         if(s instanceof Date){
18884             return s.getTime();
18885         }
18886         return Date.parse(String(s));
18887     },
18888     
18889     /**
18890      * Float sorting
18891      * @param {Mixed} s The value being converted
18892      * @return {Float} The comparison value
18893      */
18894     asFloat : function(s) {
18895         var val = parseFloat(String(s).replace(/,/g, ""));
18896         if(isNaN(val)) val = 0;
18897         return val;
18898     },
18899     
18900     /**
18901      * Integer sorting
18902      * @param {Mixed} s The value being converted
18903      * @return {Number} The comparison value
18904      */
18905     asInt : function(s) {
18906         var val = parseInt(String(s).replace(/,/g, ""));
18907         if(isNaN(val)) val = 0;
18908         return val;
18909     }
18910 };/*
18911  * Based on:
18912  * Ext JS Library 1.1.1
18913  * Copyright(c) 2006-2007, Ext JS, LLC.
18914  *
18915  * Originally Released Under LGPL - original licence link has changed is not relivant.
18916  *
18917  * Fork - LGPL
18918  * <script type="text/javascript">
18919  */
18920
18921 /**
18922 * @class Roo.data.Record
18923  * Instances of this class encapsulate both record <em>definition</em> information, and record
18924  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18925  * to access Records cached in an {@link Roo.data.Store} object.<br>
18926  * <p>
18927  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18928  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18929  * objects.<br>
18930  * <p>
18931  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18932  * @constructor
18933  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18934  * {@link #create}. The parameters are the same.
18935  * @param {Array} data An associative Array of data values keyed by the field name.
18936  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18937  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18938  * not specified an integer id is generated.
18939  */
18940 Roo.data.Record = function(data, id){
18941     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18942     this.data = data;
18943 };
18944
18945 /**
18946  * Generate a constructor for a specific record layout.
18947  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18948  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18949  * Each field definition object may contain the following properties: <ul>
18950  * <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,
18951  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18952  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18953  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18954  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18955  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18956  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18957  * this may be omitted.</p></li>
18958  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18959  * <ul><li>auto (Default, implies no conversion)</li>
18960  * <li>string</li>
18961  * <li>int</li>
18962  * <li>float</li>
18963  * <li>boolean</li>
18964  * <li>date</li></ul></p></li>
18965  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18966  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18967  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18968  * by the Reader into an object that will be stored in the Record. It is passed the
18969  * following parameters:<ul>
18970  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18971  * </ul></p></li>
18972  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18973  * </ul>
18974  * <br>usage:<br><pre><code>
18975 var TopicRecord = Roo.data.Record.create(
18976     {name: 'title', mapping: 'topic_title'},
18977     {name: 'author', mapping: 'username'},
18978     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18979     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18980     {name: 'lastPoster', mapping: 'user2'},
18981     {name: 'excerpt', mapping: 'post_text'}
18982 );
18983
18984 var myNewRecord = new TopicRecord({
18985     title: 'Do my job please',
18986     author: 'noobie',
18987     totalPosts: 1,
18988     lastPost: new Date(),
18989     lastPoster: 'Animal',
18990     excerpt: 'No way dude!'
18991 });
18992 myStore.add(myNewRecord);
18993 </code></pre>
18994  * @method create
18995  * @static
18996  */
18997 Roo.data.Record.create = function(o){
18998     var f = function(){
18999         f.superclass.constructor.apply(this, arguments);
19000     };
19001     Roo.extend(f, Roo.data.Record);
19002     var p = f.prototype;
19003     p.fields = new Roo.util.MixedCollection(false, function(field){
19004         return field.name;
19005     });
19006     for(var i = 0, len = o.length; i < len; i++){
19007         p.fields.add(new Roo.data.Field(o[i]));
19008     }
19009     f.getField = function(name){
19010         return p.fields.get(name);  
19011     };
19012     return f;
19013 };
19014
19015 Roo.data.Record.AUTO_ID = 1000;
19016 Roo.data.Record.EDIT = 'edit';
19017 Roo.data.Record.REJECT = 'reject';
19018 Roo.data.Record.COMMIT = 'commit';
19019
19020 Roo.data.Record.prototype = {
19021     /**
19022      * Readonly flag - true if this record has been modified.
19023      * @type Boolean
19024      */
19025     dirty : false,
19026     editing : false,
19027     error: null,
19028     modified: null,
19029
19030     // private
19031     join : function(store){
19032         this.store = store;
19033     },
19034
19035     /**
19036      * Set the named field to the specified value.
19037      * @param {String} name The name of the field to set.
19038      * @param {Object} value The value to set the field to.
19039      */
19040     set : function(name, value){
19041         if(this.data[name] == value){
19042             return;
19043         }
19044         this.dirty = true;
19045         if(!this.modified){
19046             this.modified = {};
19047         }
19048         if(typeof this.modified[name] == 'undefined'){
19049             this.modified[name] = this.data[name];
19050         }
19051         this.data[name] = value;
19052         if(!this.editing){
19053             this.store.afterEdit(this);
19054         }       
19055     },
19056
19057     /**
19058      * Get the value of the named field.
19059      * @param {String} name The name of the field to get the value of.
19060      * @return {Object} The value of the field.
19061      */
19062     get : function(name){
19063         return this.data[name]; 
19064     },
19065
19066     // private
19067     beginEdit : function(){
19068         this.editing = true;
19069         this.modified = {}; 
19070     },
19071
19072     // private
19073     cancelEdit : function(){
19074         this.editing = false;
19075         delete this.modified;
19076     },
19077
19078     // private
19079     endEdit : function(){
19080         this.editing = false;
19081         if(this.dirty && this.store){
19082             this.store.afterEdit(this);
19083         }
19084     },
19085
19086     /**
19087      * Usually called by the {@link Roo.data.Store} which owns the Record.
19088      * Rejects all changes made to the Record since either creation, or the last commit operation.
19089      * Modified fields are reverted to their original values.
19090      * <p>
19091      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19092      * of reject operations.
19093      */
19094     reject : function(){
19095         var m = this.modified;
19096         for(var n in m){
19097             if(typeof m[n] != "function"){
19098                 this.data[n] = m[n];
19099             }
19100         }
19101         this.dirty = false;
19102         delete this.modified;
19103         this.editing = false;
19104         if(this.store){
19105             this.store.afterReject(this);
19106         }
19107     },
19108
19109     /**
19110      * Usually called by the {@link Roo.data.Store} which owns the Record.
19111      * Commits all changes made to the Record since either creation, or the last commit operation.
19112      * <p>
19113      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19114      * of commit operations.
19115      */
19116     commit : function(){
19117         this.dirty = false;
19118         delete this.modified;
19119         this.editing = false;
19120         if(this.store){
19121             this.store.afterCommit(this);
19122         }
19123     },
19124
19125     // private
19126     hasError : function(){
19127         return this.error != null;
19128     },
19129
19130     // private
19131     clearError : function(){
19132         this.error = null;
19133     },
19134
19135     /**
19136      * Creates a copy of this record.
19137      * @param {String} id (optional) A new record id if you don't want to use this record's id
19138      * @return {Record}
19139      */
19140     copy : function(newId) {
19141         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19142     }
19143 };/*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153
19154
19155
19156 /**
19157  * @class Roo.data.Store
19158  * @extends Roo.util.Observable
19159  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19160  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19161  * <p>
19162  * 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
19163  * has no knowledge of the format of the data returned by the Proxy.<br>
19164  * <p>
19165  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19166  * instances from the data object. These records are cached and made available through accessor functions.
19167  * @constructor
19168  * Creates a new Store.
19169  * @param {Object} config A config object containing the objects needed for the Store to access data,
19170  * and read the data into Records.
19171  */
19172 Roo.data.Store = function(config){
19173     this.data = new Roo.util.MixedCollection(false);
19174     this.data.getKey = function(o){
19175         return o.id;
19176     };
19177     this.baseParams = {};
19178     // private
19179     this.paramNames = {
19180         "start" : "start",
19181         "limit" : "limit",
19182         "sort" : "sort",
19183         "dir" : "dir",
19184         "multisort" : "_multisort"
19185     };
19186
19187     if(config && config.data){
19188         this.inlineData = config.data;
19189         delete config.data;
19190     }
19191
19192     Roo.apply(this, config);
19193     
19194     if(this.reader){ // reader passed
19195         this.reader = Roo.factory(this.reader, Roo.data);
19196         this.reader.xmodule = this.xmodule || false;
19197         if(!this.recordType){
19198             this.recordType = this.reader.recordType;
19199         }
19200         if(this.reader.onMetaChange){
19201             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19202         }
19203     }
19204
19205     if(this.recordType){
19206         this.fields = this.recordType.prototype.fields;
19207     }
19208     this.modified = [];
19209
19210     this.addEvents({
19211         /**
19212          * @event datachanged
19213          * Fires when the data cache has changed, and a widget which is using this Store
19214          * as a Record cache should refresh its view.
19215          * @param {Store} this
19216          */
19217         datachanged : true,
19218         /**
19219          * @event metachange
19220          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19221          * @param {Store} this
19222          * @param {Object} meta The JSON metadata
19223          */
19224         metachange : true,
19225         /**
19226          * @event add
19227          * Fires when Records have been added to the Store
19228          * @param {Store} this
19229          * @param {Roo.data.Record[]} records The array of Records added
19230          * @param {Number} index The index at which the record(s) were added
19231          */
19232         add : true,
19233         /**
19234          * @event remove
19235          * Fires when a Record has been removed from the Store
19236          * @param {Store} this
19237          * @param {Roo.data.Record} record The Record that was removed
19238          * @param {Number} index The index at which the record was removed
19239          */
19240         remove : true,
19241         /**
19242          * @event update
19243          * Fires when a Record has been updated
19244          * @param {Store} this
19245          * @param {Roo.data.Record} record The Record that was updated
19246          * @param {String} operation The update operation being performed.  Value may be one of:
19247          * <pre><code>
19248  Roo.data.Record.EDIT
19249  Roo.data.Record.REJECT
19250  Roo.data.Record.COMMIT
19251          * </code></pre>
19252          */
19253         update : true,
19254         /**
19255          * @event clear
19256          * Fires when the data cache has been cleared.
19257          * @param {Store} this
19258          */
19259         clear : true,
19260         /**
19261          * @event beforeload
19262          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19263          * the load action will be canceled.
19264          * @param {Store} this
19265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19266          */
19267         beforeload : true,
19268         /**
19269          * @event load
19270          * Fires after a new set of Records has been loaded.
19271          * @param {Store} this
19272          * @param {Roo.data.Record[]} records The Records that were loaded
19273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19274          */
19275         load : true,
19276         /**
19277          * @event loadexception
19278          * Fires if an exception occurs in the Proxy during loading.
19279          * Called with the signature of the Proxy's "loadexception" event.
19280          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19281          * 
19282          * @param {Proxy} 
19283          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19284          * @param {Object} load options 
19285          * @param {Object} jsonData from your request (normally this contains the Exception)
19286          */
19287         loadexception : true
19288     });
19289     
19290     if(this.proxy){
19291         this.proxy = Roo.factory(this.proxy, Roo.data);
19292         this.proxy.xmodule = this.xmodule || false;
19293         this.relayEvents(this.proxy,  ["loadexception"]);
19294     }
19295     this.sortToggle = {};
19296     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19297
19298     Roo.data.Store.superclass.constructor.call(this);
19299
19300     if(this.inlineData){
19301         this.loadData(this.inlineData);
19302         delete this.inlineData;
19303     }
19304 };
19305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19306      /**
19307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19308     * without a remote query - used by combo/forms at present.
19309     */
19310     
19311     /**
19312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19313     */
19314     /**
19315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19316     */
19317     /**
19318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19320     */
19321     /**
19322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19323     * on any HTTP request
19324     */
19325     /**
19326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19327     */
19328     /**
19329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19330     */
19331     multiSort: false,
19332     /**
19333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19335     */
19336     remoteSort : false,
19337
19338     /**
19339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19340      * loaded or when a record is removed. (defaults to false).
19341     */
19342     pruneModifiedRecords : false,
19343
19344     // private
19345     lastOptions : null,
19346
19347     /**
19348      * Add Records to the Store and fires the add event.
19349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19350      */
19351     add : function(records){
19352         records = [].concat(records);
19353         for(var i = 0, len = records.length; i < len; i++){
19354             records[i].join(this);
19355         }
19356         var index = this.data.length;
19357         this.data.addAll(records);
19358         this.fireEvent("add", this, records, index);
19359     },
19360
19361     /**
19362      * Remove a Record from the Store and fires the remove event.
19363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19364      */
19365     remove : function(record){
19366         var index = this.data.indexOf(record);
19367         this.data.removeAt(index);
19368         if(this.pruneModifiedRecords){
19369             this.modified.remove(record);
19370         }
19371         this.fireEvent("remove", this, record, index);
19372     },
19373
19374     /**
19375      * Remove all Records from the Store and fires the clear event.
19376      */
19377     removeAll : function(){
19378         this.data.clear();
19379         if(this.pruneModifiedRecords){
19380             this.modified = [];
19381         }
19382         this.fireEvent("clear", this);
19383     },
19384
19385     /**
19386      * Inserts Records to the Store at the given index and fires the add event.
19387      * @param {Number} index The start index at which to insert the passed Records.
19388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19389      */
19390     insert : function(index, records){
19391         records = [].concat(records);
19392         for(var i = 0, len = records.length; i < len; i++){
19393             this.data.insert(index, records[i]);
19394             records[i].join(this);
19395         }
19396         this.fireEvent("add", this, records, index);
19397     },
19398
19399     /**
19400      * Get the index within the cache of the passed Record.
19401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19402      * @return {Number} The index of the passed Record. Returns -1 if not found.
19403      */
19404     indexOf : function(record){
19405         return this.data.indexOf(record);
19406     },
19407
19408     /**
19409      * Get the index within the cache of the Record with the passed id.
19410      * @param {String} id The id of the Record to find.
19411      * @return {Number} The index of the Record. Returns -1 if not found.
19412      */
19413     indexOfId : function(id){
19414         return this.data.indexOfKey(id);
19415     },
19416
19417     /**
19418      * Get the Record with the specified id.
19419      * @param {String} id The id of the Record to find.
19420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19421      */
19422     getById : function(id){
19423         return this.data.key(id);
19424     },
19425
19426     /**
19427      * Get the Record at the specified index.
19428      * @param {Number} index The index of the Record to find.
19429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19430      */
19431     getAt : function(index){
19432         return this.data.itemAt(index);
19433     },
19434
19435     /**
19436      * Returns a range of Records between specified indices.
19437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19439      * @return {Roo.data.Record[]} An array of Records
19440      */
19441     getRange : function(start, end){
19442         return this.data.getRange(start, end);
19443     },
19444
19445     // private
19446     storeOptions : function(o){
19447         o = Roo.apply({}, o);
19448         delete o.callback;
19449         delete o.scope;
19450         this.lastOptions = o;
19451     },
19452
19453     /**
19454      * Loads the Record cache from the configured Proxy using the configured Reader.
19455      * <p>
19456      * If using remote paging, then the first load call must specify the <em>start</em>
19457      * and <em>limit</em> properties in the options.params property to establish the initial
19458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19459      * <p>
19460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19461      * and this call will return before the new data has been loaded. Perform any post-processing
19462      * in a callback function, or in a "load" event handler.</strong>
19463      * <p>
19464      * @param {Object} options An object containing properties which control loading options:<ul>
19465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19467      * passed the following arguments:<ul>
19468      * <li>r : Roo.data.Record[]</li>
19469      * <li>options: Options object from the load call</li>
19470      * <li>success: Boolean success indicator</li></ul></li>
19471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19473      * </ul>
19474      */
19475     load : function(options){
19476         options = options || {};
19477         if(this.fireEvent("beforeload", this, options) !== false){
19478             this.storeOptions(options);
19479             var p = Roo.apply(options.params || {}, this.baseParams);
19480             // if meta was not loaded from remote source.. try requesting it.
19481             if (!this.reader.metaFromRemote) {
19482                 p._requestMeta = 1;
19483             }
19484             if(this.sortInfo && this.remoteSort){
19485                 var pn = this.paramNames;
19486                 p[pn["sort"]] = this.sortInfo.field;
19487                 p[pn["dir"]] = this.sortInfo.direction;
19488             }
19489             if (this.multiSort) {
19490                 var pn = this.paramNames;
19491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19492             }
19493             
19494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19495         }
19496     },
19497
19498     /**
19499      * Reloads the Record cache from the configured Proxy using the configured Reader and
19500      * the options from the last load operation performed.
19501      * @param {Object} options (optional) An object containing properties which may override the options
19502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19503      * the most recently used options are reused).
19504      */
19505     reload : function(options){
19506         this.load(Roo.applyIf(options||{}, this.lastOptions));
19507     },
19508
19509     // private
19510     // Called as a callback by the Reader during a load operation.
19511     loadRecords : function(o, options, success){
19512         if(!o || success === false){
19513             if(success !== false){
19514                 this.fireEvent("load", this, [], options);
19515             }
19516             if(options.callback){
19517                 options.callback.call(options.scope || this, [], options, false);
19518             }
19519             return;
19520         }
19521         // if data returned failure - throw an exception.
19522         if (o.success === false) {
19523             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19524             return;
19525         }
19526         var r = o.records, t = o.totalRecords || r.length;
19527         if(!options || options.add !== true){
19528             if(this.pruneModifiedRecords){
19529                 this.modified = [];
19530             }
19531             for(var i = 0, len = r.length; i < len; i++){
19532                 r[i].join(this);
19533             }
19534             if(this.snapshot){
19535                 this.data = this.snapshot;
19536                 delete this.snapshot;
19537             }
19538             this.data.clear();
19539             this.data.addAll(r);
19540             this.totalLength = t;
19541             this.applySort();
19542             this.fireEvent("datachanged", this);
19543         }else{
19544             this.totalLength = Math.max(t, this.data.length+r.length);
19545             this.add(r);
19546         }
19547         this.fireEvent("load", this, r, options);
19548         if(options.callback){
19549             options.callback.call(options.scope || this, r, options, true);
19550         }
19551     },
19552
19553     /**
19554      * Loads data from a passed data block. A Reader which understands the format of the data
19555      * must have been configured in the constructor.
19556      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19557      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19558      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19559      */
19560     loadData : function(o, append){
19561         var r = this.reader.readRecords(o);
19562         this.loadRecords(r, {add: append}, true);
19563     },
19564
19565     /**
19566      * Gets the number of cached records.
19567      * <p>
19568      * <em>If using paging, this may not be the total size of the dataset. If the data object
19569      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19570      * the data set size</em>
19571      */
19572     getCount : function(){
19573         return this.data.length || 0;
19574     },
19575
19576     /**
19577      * Gets the total number of records in the dataset as returned by the server.
19578      * <p>
19579      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19580      * the dataset size</em>
19581      */
19582     getTotalCount : function(){
19583         return this.totalLength || 0;
19584     },
19585
19586     /**
19587      * Returns the sort state of the Store as an object with two properties:
19588      * <pre><code>
19589  field {String} The name of the field by which the Records are sorted
19590  direction {String} The sort order, "ASC" or "DESC"
19591      * </code></pre>
19592      */
19593     getSortState : function(){
19594         return this.sortInfo;
19595     },
19596
19597     // private
19598     applySort : function(){
19599         if(this.sortInfo && !this.remoteSort){
19600             var s = this.sortInfo, f = s.field;
19601             var st = this.fields.get(f).sortType;
19602             var fn = function(r1, r2){
19603                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19604                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19605             };
19606             this.data.sort(s.direction, fn);
19607             if(this.snapshot && this.snapshot != this.data){
19608                 this.snapshot.sort(s.direction, fn);
19609             }
19610         }
19611     },
19612
19613     /**
19614      * Sets the default sort column and order to be used by the next load operation.
19615      * @param {String} fieldName The name of the field to sort by.
19616      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19617      */
19618     setDefaultSort : function(field, dir){
19619         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19620     },
19621
19622     /**
19623      * Sort the Records.
19624      * If remote sorting is used, the sort is performed on the server, and the cache is
19625      * reloaded. If local sorting is used, the cache is sorted internally.
19626      * @param {String} fieldName The name of the field to sort by.
19627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19628      */
19629     sort : function(fieldName, dir){
19630         var f = this.fields.get(fieldName);
19631         if(!dir){
19632             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19633             
19634             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19635                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19636             }else{
19637                 dir = f.sortDir;
19638             }
19639         }
19640         this.sortToggle[f.name] = dir;
19641         this.sortInfo = {field: f.name, direction: dir};
19642         if(!this.remoteSort){
19643             this.applySort();
19644             this.fireEvent("datachanged", this);
19645         }else{
19646             this.load(this.lastOptions);
19647         }
19648     },
19649
19650     /**
19651      * Calls the specified function for each of the Records in the cache.
19652      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19653      * Returning <em>false</em> aborts and exits the iteration.
19654      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19655      */
19656     each : function(fn, scope){
19657         this.data.each(fn, scope);
19658     },
19659
19660     /**
19661      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19662      * (e.g., during paging).
19663      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19664      */
19665     getModifiedRecords : function(){
19666         return this.modified;
19667     },
19668
19669     // private
19670     createFilterFn : function(property, value, anyMatch){
19671         if(!value.exec){ // not a regex
19672             value = String(value);
19673             if(value.length == 0){
19674                 return false;
19675             }
19676             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19677         }
19678         return function(r){
19679             return value.test(r.data[property]);
19680         };
19681     },
19682
19683     /**
19684      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19685      * @param {String} property A field on your records
19686      * @param {Number} start The record index to start at (defaults to 0)
19687      * @param {Number} end The last record index to include (defaults to length - 1)
19688      * @return {Number} The sum
19689      */
19690     sum : function(property, start, end){
19691         var rs = this.data.items, v = 0;
19692         start = start || 0;
19693         end = (end || end === 0) ? end : rs.length-1;
19694
19695         for(var i = start; i <= end; i++){
19696             v += (rs[i].data[property] || 0);
19697         }
19698         return v;
19699     },
19700
19701     /**
19702      * Filter the records by a specified property.
19703      * @param {String} field A field on your records
19704      * @param {String/RegExp} value Either a string that the field
19705      * should start with or a RegExp to test against the field
19706      * @param {Boolean} anyMatch True to match any part not just the beginning
19707      */
19708     filter : function(property, value, anyMatch){
19709         var fn = this.createFilterFn(property, value, anyMatch);
19710         return fn ? this.filterBy(fn) : this.clearFilter();
19711     },
19712
19713     /**
19714      * Filter by a function. The specified function will be called with each
19715      * record in this data source. If the function returns true the record is included,
19716      * otherwise it is filtered.
19717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19718      * @param {Object} scope (optional) The scope of the function (defaults to this)
19719      */
19720     filterBy : function(fn, scope){
19721         this.snapshot = this.snapshot || this.data;
19722         this.data = this.queryBy(fn, scope||this);
19723         this.fireEvent("datachanged", this);
19724     },
19725
19726     /**
19727      * Query the records by a specified property.
19728      * @param {String} field A field on your records
19729      * @param {String/RegExp} value Either a string that the field
19730      * should start with or a RegExp to test against the field
19731      * @param {Boolean} anyMatch True to match any part not just the beginning
19732      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19733      */
19734     query : function(property, value, anyMatch){
19735         var fn = this.createFilterFn(property, value, anyMatch);
19736         return fn ? this.queryBy(fn) : this.data.clone();
19737     },
19738
19739     /**
19740      * Query by a function. The specified function will be called with each
19741      * record in this data source. If the function returns true the record is included
19742      * in the results.
19743      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19744      * @param {Object} scope (optional) The scope of the function (defaults to this)
19745       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19746      **/
19747     queryBy : function(fn, scope){
19748         var data = this.snapshot || this.data;
19749         return data.filterBy(fn, scope||this);
19750     },
19751
19752     /**
19753      * Collects unique values for a particular dataIndex from this store.
19754      * @param {String} dataIndex The property to collect
19755      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19756      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19757      * @return {Array} An array of the unique values
19758      **/
19759     collect : function(dataIndex, allowNull, bypassFilter){
19760         var d = (bypassFilter === true && this.snapshot) ?
19761                 this.snapshot.items : this.data.items;
19762         var v, sv, r = [], l = {};
19763         for(var i = 0, len = d.length; i < len; i++){
19764             v = d[i].data[dataIndex];
19765             sv = String(v);
19766             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19767                 l[sv] = true;
19768                 r[r.length] = v;
19769             }
19770         }
19771         return r;
19772     },
19773
19774     /**
19775      * Revert to a view of the Record cache with no filtering applied.
19776      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19777      */
19778     clearFilter : function(suppressEvent){
19779         if(this.snapshot && this.snapshot != this.data){
19780             this.data = this.snapshot;
19781             delete this.snapshot;
19782             if(suppressEvent !== true){
19783                 this.fireEvent("datachanged", this);
19784             }
19785         }
19786     },
19787
19788     // private
19789     afterEdit : function(record){
19790         if(this.modified.indexOf(record) == -1){
19791             this.modified.push(record);
19792         }
19793         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19794     },
19795
19796     // private
19797     afterReject : function(record){
19798         this.modified.remove(record);
19799         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19800     },
19801
19802     // private
19803     afterCommit : function(record){
19804         this.modified.remove(record);
19805         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19806     },
19807
19808     /**
19809      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19810      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19811      */
19812     commitChanges : function(){
19813         var m = this.modified.slice(0);
19814         this.modified = [];
19815         for(var i = 0, len = m.length; i < len; i++){
19816             m[i].commit();
19817         }
19818     },
19819
19820     /**
19821      * Cancel outstanding changes on all changed records.
19822      */
19823     rejectChanges : function(){
19824         var m = this.modified.slice(0);
19825         this.modified = [];
19826         for(var i = 0, len = m.length; i < len; i++){
19827             m[i].reject();
19828         }
19829     },
19830
19831     onMetaChange : function(meta, rtype, o){
19832         this.recordType = rtype;
19833         this.fields = rtype.prototype.fields;
19834         delete this.snapshot;
19835         this.sortInfo = meta.sortInfo || this.sortInfo;
19836         this.modified = [];
19837         this.fireEvent('metachange', this, this.reader.meta);
19838     }
19839 });/*
19840  * Based on:
19841  * Ext JS Library 1.1.1
19842  * Copyright(c) 2006-2007, Ext JS, LLC.
19843  *
19844  * Originally Released Under LGPL - original licence link has changed is not relivant.
19845  *
19846  * Fork - LGPL
19847  * <script type="text/javascript">
19848  */
19849
19850 /**
19851  * @class Roo.data.SimpleStore
19852  * @extends Roo.data.Store
19853  * Small helper class to make creating Stores from Array data easier.
19854  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19855  * @cfg {Array} fields An array of field definition objects, or field name strings.
19856  * @cfg {Array} data The multi-dimensional array of data
19857  * @constructor
19858  * @param {Object} config
19859  */
19860 Roo.data.SimpleStore = function(config){
19861     Roo.data.SimpleStore.superclass.constructor.call(this, {
19862         isLocal : true,
19863         reader: new Roo.data.ArrayReader({
19864                 id: config.id
19865             },
19866             Roo.data.Record.create(config.fields)
19867         ),
19868         proxy : new Roo.data.MemoryProxy(config.data)
19869     });
19870     this.load();
19871 };
19872 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
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 /**
19885  * @extends Roo.data.Store
19886  * @class Roo.data.JsonStore
19887  * Small helper class to make creating Stores for JSON data easier. <br/>
19888 <pre><code>
19889 var store = new Roo.data.JsonStore({
19890     url: 'get-images.php',
19891     root: 'images',
19892     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19893 });
19894 </code></pre>
19895  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19896  * JsonReader and HttpProxy (unless inline data is provided).</b>
19897  * @cfg {Array} fields An array of field definition objects, or field name strings.
19898  * @constructor
19899  * @param {Object} config
19900  */
19901 Roo.data.JsonStore = function(c){
19902     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19903         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19904         reader: new Roo.data.JsonReader(c, c.fields)
19905     }));
19906 };
19907 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19908  * Based on:
19909  * Ext JS Library 1.1.1
19910  * Copyright(c) 2006-2007, Ext JS, LLC.
19911  *
19912  * Originally Released Under LGPL - original licence link has changed is not relivant.
19913  *
19914  * Fork - LGPL
19915  * <script type="text/javascript">
19916  */
19917
19918  
19919 Roo.data.Field = function(config){
19920     if(typeof config == "string"){
19921         config = {name: config};
19922     }
19923     Roo.apply(this, config);
19924     
19925     if(!this.type){
19926         this.type = "auto";
19927     }
19928     
19929     var st = Roo.data.SortTypes;
19930     // named sortTypes are supported, here we look them up
19931     if(typeof this.sortType == "string"){
19932         this.sortType = st[this.sortType];
19933     }
19934     
19935     // set default sortType for strings and dates
19936     if(!this.sortType){
19937         switch(this.type){
19938             case "string":
19939                 this.sortType = st.asUCString;
19940                 break;
19941             case "date":
19942                 this.sortType = st.asDate;
19943                 break;
19944             default:
19945                 this.sortType = st.none;
19946         }
19947     }
19948
19949     // define once
19950     var stripRe = /[\$,%]/g;
19951
19952     // prebuilt conversion function for this field, instead of
19953     // switching every time we're reading a value
19954     if(!this.convert){
19955         var cv, dateFormat = this.dateFormat;
19956         switch(this.type){
19957             case "":
19958             case "auto":
19959             case undefined:
19960                 cv = function(v){ return v; };
19961                 break;
19962             case "string":
19963                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19964                 break;
19965             case "int":
19966                 cv = function(v){
19967                     return v !== undefined && v !== null && v !== '' ?
19968                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19969                     };
19970                 break;
19971             case "float":
19972                 cv = function(v){
19973                     return v !== undefined && v !== null && v !== '' ?
19974                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19975                     };
19976                 break;
19977             case "bool":
19978             case "boolean":
19979                 cv = function(v){ return v === true || v === "true" || v == 1; };
19980                 break;
19981             case "date":
19982                 cv = function(v){
19983                     if(!v){
19984                         return '';
19985                     }
19986                     if(v instanceof Date){
19987                         return v;
19988                     }
19989                     if(dateFormat){
19990                         if(dateFormat == "timestamp"){
19991                             return new Date(v*1000);
19992                         }
19993                         return Date.parseDate(v, dateFormat);
19994                     }
19995                     var parsed = Date.parse(v);
19996                     return parsed ? new Date(parsed) : null;
19997                 };
19998              break;
19999             
20000         }
20001         this.convert = cv;
20002     }
20003 };
20004
20005 Roo.data.Field.prototype = {
20006     dateFormat: null,
20007     defaultValue: "",
20008     mapping: null,
20009     sortType : null,
20010     sortDir : "ASC"
20011 };/*
20012  * Based on:
20013  * Ext JS Library 1.1.1
20014  * Copyright(c) 2006-2007, Ext JS, LLC.
20015  *
20016  * Originally Released Under LGPL - original licence link has changed is not relivant.
20017  *
20018  * Fork - LGPL
20019  * <script type="text/javascript">
20020  */
20021  
20022 // Base class for reading structured data from a data source.  This class is intended to be
20023 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20024
20025 /**
20026  * @class Roo.data.DataReader
20027  * Base class for reading structured data from a data source.  This class is intended to be
20028  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20029  */
20030
20031 Roo.data.DataReader = function(meta, recordType){
20032     
20033     this.meta = meta;
20034     
20035     this.recordType = recordType instanceof Array ? 
20036         Roo.data.Record.create(recordType) : recordType;
20037 };
20038
20039 Roo.data.DataReader.prototype = {
20040      /**
20041      * Create an empty record
20042      * @param {Object} data (optional) - overlay some values
20043      * @return {Roo.data.Record} record created.
20044      */
20045     newRow :  function(d) {
20046         var da =  {};
20047         this.recordType.prototype.fields.each(function(c) {
20048             switch( c.type) {
20049                 case 'int' : da[c.name] = 0; break;
20050                 case 'date' : da[c.name] = new Date(); break;
20051                 case 'float' : da[c.name] = 0.0; break;
20052                 case 'boolean' : da[c.name] = false; break;
20053                 default : da[c.name] = ""; break;
20054             }
20055             
20056         });
20057         return new this.recordType(Roo.apply(da, d));
20058     }
20059     
20060 };/*
20061  * Based on:
20062  * Ext JS Library 1.1.1
20063  * Copyright(c) 2006-2007, Ext JS, LLC.
20064  *
20065  * Originally Released Under LGPL - original licence link has changed is not relivant.
20066  *
20067  * Fork - LGPL
20068  * <script type="text/javascript">
20069  */
20070
20071 /**
20072  * @class Roo.data.DataProxy
20073  * @extends Roo.data.Observable
20074  * This class is an abstract base class for implementations which provide retrieval of
20075  * unformatted data objects.<br>
20076  * <p>
20077  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20078  * (of the appropriate type which knows how to parse the data object) to provide a block of
20079  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20080  * <p>
20081  * Custom implementations must implement the load method as described in
20082  * {@link Roo.data.HttpProxy#load}.
20083  */
20084 Roo.data.DataProxy = function(){
20085     this.addEvents({
20086         /**
20087          * @event beforeload
20088          * Fires before a network request is made to retrieve a data object.
20089          * @param {Object} This DataProxy object.
20090          * @param {Object} params The params parameter to the load function.
20091          */
20092         beforeload : true,
20093         /**
20094          * @event load
20095          * Fires before the load method's callback is called.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} o The data object.
20098          * @param {Object} arg The callback argument object passed to the load function.
20099          */
20100         load : true,
20101         /**
20102          * @event loadexception
20103          * Fires if an Exception occurs during data retrieval.
20104          * @param {Object} This DataProxy object.
20105          * @param {Object} o The data object.
20106          * @param {Object} arg The callback argument object passed to the load function.
20107          * @param {Object} e The Exception.
20108          */
20109         loadexception : true
20110     });
20111     Roo.data.DataProxy.superclass.constructor.call(this);
20112 };
20113
20114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20115
20116     /**
20117      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20118      */
20119 /*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129 /**
20130  * @class Roo.data.MemoryProxy
20131  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20132  * to the Reader when its load method is called.
20133  * @constructor
20134  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20135  */
20136 Roo.data.MemoryProxy = function(data){
20137     if (data.data) {
20138         data = data.data;
20139     }
20140     Roo.data.MemoryProxy.superclass.constructor.call(this);
20141     this.data = data;
20142 };
20143
20144 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20145     /**
20146      * Load data from the requested source (in this case an in-memory
20147      * data object passed to the constructor), read the data object into
20148      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20149      * process that block using the passed callback.
20150      * @param {Object} params This parameter is not used by the MemoryProxy class.
20151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20152      * object into a block of Roo.data.Records.
20153      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20154      * The function must be passed <ul>
20155      * <li>The Record block object</li>
20156      * <li>The "arg" argument from the load function</li>
20157      * <li>A boolean success indicator</li>
20158      * </ul>
20159      * @param {Object} scope The scope in which to call the callback
20160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20161      */
20162     load : function(params, reader, callback, scope, arg){
20163         params = params || {};
20164         var result;
20165         try {
20166             result = reader.readRecords(this.data);
20167         }catch(e){
20168             this.fireEvent("loadexception", this, arg, null, e);
20169             callback.call(scope, null, arg, false);
20170             return;
20171         }
20172         callback.call(scope, result, arg, true);
20173     },
20174     
20175     // private
20176     update : function(params, records){
20177         
20178     }
20179 });/*
20180  * Based on:
20181  * Ext JS Library 1.1.1
20182  * Copyright(c) 2006-2007, Ext JS, LLC.
20183  *
20184  * Originally Released Under LGPL - original licence link has changed is not relivant.
20185  *
20186  * Fork - LGPL
20187  * <script type="text/javascript">
20188  */
20189 /**
20190  * @class Roo.data.HttpProxy
20191  * @extends Roo.data.DataProxy
20192  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20193  * configured to reference a certain URL.<br><br>
20194  * <p>
20195  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20196  * from which the running page was served.<br><br>
20197  * <p>
20198  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20199  * <p>
20200  * Be aware that to enable the browser to parse an XML document, the server must set
20201  * the Content-Type header in the HTTP response to "text/xml".
20202  * @constructor
20203  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20204  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20205  * will be used to make the request.
20206  */
20207 Roo.data.HttpProxy = function(conn){
20208     Roo.data.HttpProxy.superclass.constructor.call(this);
20209     // is conn a conn config or a real conn?
20210     this.conn = conn;
20211     this.useAjax = !conn || !conn.events;
20212   
20213 };
20214
20215 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20216     // thse are take from connection...
20217     
20218     /**
20219      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20220      */
20221     /**
20222      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20223      * extra parameters to each request made by this object. (defaults to undefined)
20224      */
20225     /**
20226      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20227      *  to each request made by this object. (defaults to undefined)
20228      */
20229     /**
20230      * @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)
20231      */
20232     /**
20233      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20234      */
20235      /**
20236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20237      * @type Boolean
20238      */
20239   
20240
20241     /**
20242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20243      * @type Boolean
20244      */
20245     /**
20246      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20247      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20248      * a finer-grained basis than the DataProxy events.
20249      */
20250     getConnection : function(){
20251         return this.useAjax ? Roo.Ajax : this.conn;
20252     },
20253
20254     /**
20255      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20256      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20257      * process that block using the passed callback.
20258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20259      * for the request to the remote server.
20260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20261      * object into a block of Roo.data.Records.
20262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20263      * The function must be passed <ul>
20264      * <li>The Record block object</li>
20265      * <li>The "arg" argument from the load function</li>
20266      * <li>A boolean success indicator</li>
20267      * </ul>
20268      * @param {Object} scope The scope in which to call the callback
20269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20270      */
20271     load : function(params, reader, callback, scope, arg){
20272         if(this.fireEvent("beforeload", this, params) !== false){
20273             var  o = {
20274                 params : params || {},
20275                 request: {
20276                     callback : callback,
20277                     scope : scope,
20278                     arg : arg
20279                 },
20280                 reader: reader,
20281                 callback : this.loadResponse,
20282                 scope: this
20283             };
20284             if(this.useAjax){
20285                 Roo.applyIf(o, this.conn);
20286                 if(this.activeRequest){
20287                     Roo.Ajax.abort(this.activeRequest);
20288                 }
20289                 this.activeRequest = Roo.Ajax.request(o);
20290             }else{
20291                 this.conn.request(o);
20292             }
20293         }else{
20294             callback.call(scope||this, null, arg, false);
20295         }
20296     },
20297
20298     // private
20299     loadResponse : function(o, success, response){
20300         delete this.activeRequest;
20301         if(!success){
20302             this.fireEvent("loadexception", this, o, response);
20303             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20304             return;
20305         }
20306         var result;
20307         try {
20308             result = o.reader.read(response);
20309         }catch(e){
20310             this.fireEvent("loadexception", this, o, response, e);
20311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20312             return;
20313         }
20314         
20315         this.fireEvent("load", this, o, o.request.arg);
20316         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20317     },
20318
20319     // private
20320     update : function(dataSet){
20321
20322     },
20323
20324     // private
20325     updateResponse : function(dataSet){
20326
20327     }
20328 });/*
20329  * Based on:
20330  * Ext JS Library 1.1.1
20331  * Copyright(c) 2006-2007, Ext JS, LLC.
20332  *
20333  * Originally Released Under LGPL - original licence link has changed is not relivant.
20334  *
20335  * Fork - LGPL
20336  * <script type="text/javascript">
20337  */
20338
20339 /**
20340  * @class Roo.data.ScriptTagProxy
20341  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20342  * other than the originating domain of the running page.<br><br>
20343  * <p>
20344  * <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
20345  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20346  * <p>
20347  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20348  * source code that is used as the source inside a &lt;script> tag.<br><br>
20349  * <p>
20350  * In order for the browser to process the returned data, the server must wrap the data object
20351  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20352  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20353  * depending on whether the callback name was passed:
20354  * <p>
20355  * <pre><code>
20356 boolean scriptTag = false;
20357 String cb = request.getParameter("callback");
20358 if (cb != null) {
20359     scriptTag = true;
20360     response.setContentType("text/javascript");
20361 } else {
20362     response.setContentType("application/x-json");
20363 }
20364 Writer out = response.getWriter();
20365 if (scriptTag) {
20366     out.write(cb + "(");
20367 }
20368 out.print(dataBlock.toJsonString());
20369 if (scriptTag) {
20370     out.write(");");
20371 }
20372 </pre></code>
20373  *
20374  * @constructor
20375  * @param {Object} config A configuration object.
20376  */
20377 Roo.data.ScriptTagProxy = function(config){
20378     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20379     Roo.apply(this, config);
20380     this.head = document.getElementsByTagName("head")[0];
20381 };
20382
20383 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20384
20385 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20386     /**
20387      * @cfg {String} url The URL from which to request the data object.
20388      */
20389     /**
20390      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20391      */
20392     timeout : 30000,
20393     /**
20394      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20395      * the server the name of the callback function set up by the load call to process the returned data object.
20396      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20397      * javascript output which calls this named function passing the data object as its only parameter.
20398      */
20399     callbackParam : "callback",
20400     /**
20401      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20402      * name to the request.
20403      */
20404     nocache : true,
20405
20406     /**
20407      * Load data from the configured URL, read the data object into
20408      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20409      * process that block using the passed callback.
20410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20411      * for the request to the remote server.
20412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20413      * object into a block of Roo.data.Records.
20414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20415      * The function must be passed <ul>
20416      * <li>The Record block object</li>
20417      * <li>The "arg" argument from the load function</li>
20418      * <li>A boolean success indicator</li>
20419      * </ul>
20420      * @param {Object} scope The scope in which to call the callback
20421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20422      */
20423     load : function(params, reader, callback, scope, arg){
20424         if(this.fireEvent("beforeload", this, params) !== false){
20425
20426             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20427
20428             var url = this.url;
20429             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20430             if(this.nocache){
20431                 url += "&_dc=" + (new Date().getTime());
20432             }
20433             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20434             var trans = {
20435                 id : transId,
20436                 cb : "stcCallback"+transId,
20437                 scriptId : "stcScript"+transId,
20438                 params : params,
20439                 arg : arg,
20440                 url : url,
20441                 callback : callback,
20442                 scope : scope,
20443                 reader : reader
20444             };
20445             var conn = this;
20446
20447             window[trans.cb] = function(o){
20448                 conn.handleResponse(o, trans);
20449             };
20450
20451             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20452
20453             if(this.autoAbort !== false){
20454                 this.abort();
20455             }
20456
20457             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20458
20459             var script = document.createElement("script");
20460             script.setAttribute("src", url);
20461             script.setAttribute("type", "text/javascript");
20462             script.setAttribute("id", trans.scriptId);
20463             this.head.appendChild(script);
20464
20465             this.trans = trans;
20466         }else{
20467             callback.call(scope||this, null, arg, false);
20468         }
20469     },
20470
20471     // private
20472     isLoading : function(){
20473         return this.trans ? true : false;
20474     },
20475
20476     /**
20477      * Abort the current server request.
20478      */
20479     abort : function(){
20480         if(this.isLoading()){
20481             this.destroyTrans(this.trans);
20482         }
20483     },
20484
20485     // private
20486     destroyTrans : function(trans, isLoaded){
20487         this.head.removeChild(document.getElementById(trans.scriptId));
20488         clearTimeout(trans.timeoutId);
20489         if(isLoaded){
20490             window[trans.cb] = undefined;
20491             try{
20492                 delete window[trans.cb];
20493             }catch(e){}
20494         }else{
20495             // if hasn't been loaded, wait for load to remove it to prevent script error
20496             window[trans.cb] = function(){
20497                 window[trans.cb] = undefined;
20498                 try{
20499                     delete window[trans.cb];
20500                 }catch(e){}
20501             };
20502         }
20503     },
20504
20505     // private
20506     handleResponse : function(o, trans){
20507         this.trans = false;
20508         this.destroyTrans(trans, true);
20509         var result;
20510         try {
20511             result = trans.reader.readRecords(o);
20512         }catch(e){
20513             this.fireEvent("loadexception", this, o, trans.arg, e);
20514             trans.callback.call(trans.scope||window, null, trans.arg, false);
20515             return;
20516         }
20517         this.fireEvent("load", this, o, trans.arg);
20518         trans.callback.call(trans.scope||window, result, trans.arg, true);
20519     },
20520
20521     // private
20522     handleFailure : function(trans){
20523         this.trans = false;
20524         this.destroyTrans(trans, false);
20525         this.fireEvent("loadexception", this, null, trans.arg);
20526         trans.callback.call(trans.scope||window, null, trans.arg, false);
20527     }
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538
20539 /**
20540  * @class Roo.data.JsonReader
20541  * @extends Roo.data.DataReader
20542  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20543  * based on mappings in a provided Roo.data.Record constructor.
20544  * 
20545  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20546  * in the reply previously. 
20547  * 
20548  * <p>
20549  * Example code:
20550  * <pre><code>
20551 var RecordDef = Roo.data.Record.create([
20552     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20553     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20554 ]);
20555 var myReader = new Roo.data.JsonReader({
20556     totalProperty: "results",    // The property which contains the total dataset size (optional)
20557     root: "rows",                // The property which contains an Array of row objects
20558     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20559 }, RecordDef);
20560 </code></pre>
20561  * <p>
20562  * This would consume a JSON file like this:
20563  * <pre><code>
20564 { 'results': 2, 'rows': [
20565     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20566     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20567 }
20568 </code></pre>
20569  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20571  * paged from the remote server.
20572  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20573  * @cfg {String} root name of the property which contains the Array of row objects.
20574  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20575  * @constructor
20576  * Create a new JsonReader
20577  * @param {Object} meta Metadata configuration options
20578  * @param {Object} recordType Either an Array of field definition objects,
20579  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20580  */
20581 Roo.data.JsonReader = function(meta, recordType){
20582     
20583     meta = meta || {};
20584     // set some defaults:
20585     Roo.applyIf(meta, {
20586         totalProperty: 'total',
20587         successProperty : 'success',
20588         root : 'data',
20589         id : 'id'
20590     });
20591     
20592     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20593 };
20594 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20595     
20596     /**
20597      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20598      * Used by Store query builder to append _requestMeta to params.
20599      * 
20600      */
20601     metaFromRemote : false,
20602     /**
20603      * This method is only used by a DataProxy which has retrieved data from a remote server.
20604      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     read : function(response){
20609         var json = response.responseText;
20610        
20611         var o = /* eval:var:o */ eval("("+json+")");
20612         if(!o) {
20613             throw {message: "JsonReader.read: Json object not found"};
20614         }
20615         
20616         if(o.metaData){
20617             
20618             delete this.ef;
20619             this.metaFromRemote = true;
20620             this.meta = o.metaData;
20621             this.recordType = Roo.data.Record.create(o.metaData.fields);
20622             this.onMetaChange(this.meta, this.recordType, o);
20623         }
20624         return this.readRecords(o);
20625     },
20626
20627     // private function a store will implement
20628     onMetaChange : function(meta, recordType, o){
20629
20630     },
20631
20632     /**
20633          * @ignore
20634          */
20635     simpleAccess: function(obj, subsc) {
20636         return obj[subsc];
20637     },
20638
20639         /**
20640          * @ignore
20641          */
20642     getJsonAccessor: function(){
20643         var re = /[\[\.]/;
20644         return function(expr) {
20645             try {
20646                 return(re.test(expr))
20647                     ? new Function("obj", "return obj." + expr)
20648                     : function(obj){
20649                         return obj[expr];
20650                     };
20651             } catch(e){}
20652             return Roo.emptyFn;
20653         };
20654     }(),
20655
20656     /**
20657      * Create a data block containing Roo.data.Records from an XML document.
20658      * @param {Object} o An object which contains an Array of row objects in the property specified
20659      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20660      * which contains the total size of the dataset.
20661      * @return {Object} data A data block which is used by an Roo.data.Store object as
20662      * a cache of Roo.data.Records.
20663      */
20664     readRecords : function(o){
20665         /**
20666          * After any data loads, the raw JSON data is available for further custom processing.
20667          * @type Object
20668          */
20669         this.jsonData = o;
20670         var s = this.meta, Record = this.recordType,
20671             f = Record.prototype.fields, fi = f.items, fl = f.length;
20672
20673 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20674         if (!this.ef) {
20675             if(s.totalProperty) {
20676                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20677                 }
20678                 if(s.successProperty) {
20679                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20680                 }
20681                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20682                 if (s.id) {
20683                         var g = this.getJsonAccessor(s.id);
20684                         this.getId = function(rec) {
20685                                 var r = g(rec);
20686                                 return (r === undefined || r === "") ? null : r;
20687                         };
20688                 } else {
20689                         this.getId = function(){return null;};
20690                 }
20691             this.ef = [];
20692             for(var jj = 0; jj < fl; jj++){
20693                 f = fi[jj];
20694                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20695                 this.ef[jj] = this.getJsonAccessor(map);
20696             }
20697         }
20698
20699         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20700         if(s.totalProperty){
20701             var vt = parseInt(this.getTotal(o), 10);
20702             if(!isNaN(vt)){
20703                 totalRecords = vt;
20704             }
20705         }
20706         if(s.successProperty){
20707             var vs = this.getSuccess(o);
20708             if(vs === false || vs === 'false'){
20709                 success = false;
20710             }
20711         }
20712         var records = [];
20713             for(var i = 0; i < c; i++){
20714                     var n = root[i];
20715                 var values = {};
20716                 var id = this.getId(n);
20717                 for(var j = 0; j < fl; j++){
20718                     f = fi[j];
20719                 var v = this.ef[j](n);
20720                 if (!f.convert) {
20721                     Roo.log('missing convert for ' + f.name);
20722                     Roo.log(f);
20723                     continue;
20724                 }
20725                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20726                 }
20727                 var record = new Record(values, id);
20728                 record.json = n;
20729                 records[i] = record;
20730             }
20731             return {
20732                 success : success,
20733                 records : records,
20734                 totalRecords : totalRecords
20735             };
20736     }
20737 });/*
20738  * Based on:
20739  * Ext JS Library 1.1.1
20740  * Copyright(c) 2006-2007, Ext JS, LLC.
20741  *
20742  * Originally Released Under LGPL - original licence link has changed is not relivant.
20743  *
20744  * Fork - LGPL
20745  * <script type="text/javascript">
20746  */
20747
20748 /**
20749  * @class Roo.data.XmlReader
20750  * @extends Roo.data.DataReader
20751  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20752  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20753  * <p>
20754  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20755  * header in the HTTP response must be set to "text/xml".</em>
20756  * <p>
20757  * Example code:
20758  * <pre><code>
20759 var RecordDef = Roo.data.Record.create([
20760    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20761    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20762 ]);
20763 var myReader = new Roo.data.XmlReader({
20764    totalRecords: "results", // The element which contains the total dataset size (optional)
20765    record: "row",           // The repeated element which contains row information
20766    id: "id"                 // The element within the row that provides an ID for the record (optional)
20767 }, RecordDef);
20768 </code></pre>
20769  * <p>
20770  * This would consume an XML file like this:
20771  * <pre><code>
20772 &lt;?xml?>
20773 &lt;dataset>
20774  &lt;results>2&lt;/results>
20775  &lt;row>
20776    &lt;id>1&lt;/id>
20777    &lt;name>Bill&lt;/name>
20778    &lt;occupation>Gardener&lt;/occupation>
20779  &lt;/row>
20780  &lt;row>
20781    &lt;id>2&lt;/id>
20782    &lt;name>Ben&lt;/name>
20783    &lt;occupation>Horticulturalist&lt;/occupation>
20784  &lt;/row>
20785 &lt;/dataset>
20786 </code></pre>
20787  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20788  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20789  * paged from the remote server.
20790  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20791  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20792  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20793  * a record identifier value.
20794  * @constructor
20795  * Create a new XmlReader
20796  * @param {Object} meta Metadata configuration options
20797  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20798  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20799  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20800  */
20801 Roo.data.XmlReader = function(meta, recordType){
20802     meta = meta || {};
20803     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20804 };
20805 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20806     /**
20807      * This method is only used by a DataProxy which has retrieved data from a remote server.
20808          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20809          * to contain a method called 'responseXML' that returns an XML document object.
20810      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20811      * a cache of Roo.data.Records.
20812      */
20813     read : function(response){
20814         var doc = response.responseXML;
20815         if(!doc) {
20816             throw {message: "XmlReader.read: XML Document not available"};
20817         }
20818         return this.readRecords(doc);
20819     },
20820
20821     /**
20822      * Create a data block containing Roo.data.Records from an XML document.
20823          * @param {Object} doc A parsed XML document.
20824      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20825      * a cache of Roo.data.Records.
20826      */
20827     readRecords : function(doc){
20828         /**
20829          * After any data loads/reads, the raw XML Document is available for further custom processing.
20830          * @type XMLDocument
20831          */
20832         this.xmlData = doc;
20833         var root = doc.documentElement || doc;
20834         var q = Roo.DomQuery;
20835         var recordType = this.recordType, fields = recordType.prototype.fields;
20836         var sid = this.meta.id;
20837         var totalRecords = 0, success = true;
20838         if(this.meta.totalRecords){
20839             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20840         }
20841         
20842         if(this.meta.success){
20843             var sv = q.selectValue(this.meta.success, root, true);
20844             success = sv !== false && sv !== 'false';
20845         }
20846         var records = [];
20847         var ns = q.select(this.meta.record, root);
20848         for(var i = 0, len = ns.length; i < len; i++) {
20849                 var n = ns[i];
20850                 var values = {};
20851                 var id = sid ? q.selectValue(sid, n) : undefined;
20852                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20853                     var f = fields.items[j];
20854                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20855                     v = f.convert(v);
20856                     values[f.name] = v;
20857                 }
20858                 var record = new recordType(values, id);
20859                 record.node = n;
20860                 records[records.length] = record;
20861             }
20862
20863             return {
20864                 success : success,
20865                 records : records,
20866                 totalRecords : totalRecords || records.length
20867             };
20868     }
20869 });/*
20870  * Based on:
20871  * Ext JS Library 1.1.1
20872  * Copyright(c) 2006-2007, Ext JS, LLC.
20873  *
20874  * Originally Released Under LGPL - original licence link has changed is not relivant.
20875  *
20876  * Fork - LGPL
20877  * <script type="text/javascript">
20878  */
20879
20880 /**
20881  * @class Roo.data.ArrayReader
20882  * @extends Roo.data.DataReader
20883  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20884  * Each element of that Array represents a row of data fields. The
20885  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20886  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20887  * <p>
20888  * Example code:.
20889  * <pre><code>
20890 var RecordDef = Roo.data.Record.create([
20891     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20892     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20893 ]);
20894 var myReader = new Roo.data.ArrayReader({
20895     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20896 }, RecordDef);
20897 </code></pre>
20898  * <p>
20899  * This would consume an Array like this:
20900  * <pre><code>
20901 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20902   </code></pre>
20903  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20904  * @constructor
20905  * Create a new JsonReader
20906  * @param {Object} meta Metadata configuration options.
20907  * @param {Object} recordType Either an Array of field definition objects
20908  * as specified to {@link Roo.data.Record#create},
20909  * or an {@link Roo.data.Record} object
20910  * created using {@link Roo.data.Record#create}.
20911  */
20912 Roo.data.ArrayReader = function(meta, recordType){
20913     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20914 };
20915
20916 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20917     /**
20918      * Create a data block containing Roo.data.Records from an XML document.
20919      * @param {Object} o An Array of row objects which represents the dataset.
20920      * @return {Object} data A data block which is used by an Roo.data.Store object as
20921      * a cache of Roo.data.Records.
20922      */
20923     readRecords : function(o){
20924         var sid = this.meta ? this.meta.id : null;
20925         var recordType = this.recordType, fields = recordType.prototype.fields;
20926         var records = [];
20927         var root = o;
20928             for(var i = 0; i < root.length; i++){
20929                     var n = root[i];
20930                 var values = {};
20931                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20932                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20933                 var f = fields.items[j];
20934                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20935                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20936                 v = f.convert(v);
20937                 values[f.name] = v;
20938             }
20939                 var record = new recordType(values, id);
20940                 record.json = n;
20941                 records[records.length] = record;
20942             }
20943             return {
20944                 records : records,
20945                 totalRecords : records.length
20946             };
20947     }
20948 });/*
20949  * Based on:
20950  * Ext JS Library 1.1.1
20951  * Copyright(c) 2006-2007, Ext JS, LLC.
20952  *
20953  * Originally Released Under LGPL - original licence link has changed is not relivant.
20954  *
20955  * Fork - LGPL
20956  * <script type="text/javascript">
20957  */
20958
20959
20960 /**
20961  * @class Roo.data.Tree
20962  * @extends Roo.util.Observable
20963  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20964  * in the tree have most standard DOM functionality.
20965  * @constructor
20966  * @param {Node} root (optional) The root node
20967  */
20968 Roo.data.Tree = function(root){
20969    this.nodeHash = {};
20970    /**
20971     * The root node for this tree
20972     * @type Node
20973     */
20974    this.root = null;
20975    if(root){
20976        this.setRootNode(root);
20977    }
20978    this.addEvents({
20979        /**
20980         * @event append
20981         * Fires when a new child node is appended to a node in this tree.
20982         * @param {Tree} tree The owner tree
20983         * @param {Node} parent The parent node
20984         * @param {Node} node The newly appended node
20985         * @param {Number} index The index of the newly appended node
20986         */
20987        "append" : true,
20988        /**
20989         * @event remove
20990         * Fires when a child node is removed from a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The child node removed
20994         */
20995        "remove" : true,
20996        /**
20997         * @event move
20998         * Fires when a node is moved to a new location in the tree
20999         * @param {Tree} tree The owner tree
21000         * @param {Node} node The node moved
21001         * @param {Node} oldParent The old parent of this node
21002         * @param {Node} newParent The new parent of this node
21003         * @param {Number} index The index it was moved to
21004         */
21005        "move" : true,
21006        /**
21007         * @event insert
21008         * Fires when a new child node is inserted in a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The child node inserted
21012         * @param {Node} refNode The child node the node was inserted before
21013         */
21014        "insert" : true,
21015        /**
21016         * @event beforeappend
21017         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node to be appended
21021         */
21022        "beforeappend" : true,
21023        /**
21024         * @event beforeremove
21025         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} parent The parent node
21028         * @param {Node} node The child node to be removed
21029         */
21030        "beforeremove" : true,
21031        /**
21032         * @event beforemove
21033         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21034         * @param {Tree} tree The owner tree
21035         * @param {Node} node The node being moved
21036         * @param {Node} oldParent The parent of the node
21037         * @param {Node} newParent The new parent the node is moving to
21038         * @param {Number} index The index it is being moved to
21039         */
21040        "beforemove" : true,
21041        /**
21042         * @event beforeinsert
21043         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21044         * @param {Tree} tree The owner tree
21045         * @param {Node} parent The parent node
21046         * @param {Node} node The child node to be inserted
21047         * @param {Node} refNode The child node the node is being inserted before
21048         */
21049        "beforeinsert" : true
21050    });
21051
21052     Roo.data.Tree.superclass.constructor.call(this);
21053 };
21054
21055 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21056     pathSeparator: "/",
21057
21058     proxyNodeEvent : function(){
21059         return this.fireEvent.apply(this, arguments);
21060     },
21061
21062     /**
21063      * Returns the root node for this tree.
21064      * @return {Node}
21065      */
21066     getRootNode : function(){
21067         return this.root;
21068     },
21069
21070     /**
21071      * Sets the root node for this tree.
21072      * @param {Node} node
21073      * @return {Node}
21074      */
21075     setRootNode : function(node){
21076         this.root = node;
21077         node.ownerTree = this;
21078         node.isRoot = true;
21079         this.registerNode(node);
21080         return node;
21081     },
21082
21083     /**
21084      * Gets a node in this tree by its id.
21085      * @param {String} id
21086      * @return {Node}
21087      */
21088     getNodeById : function(id){
21089         return this.nodeHash[id];
21090     },
21091
21092     registerNode : function(node){
21093         this.nodeHash[node.id] = node;
21094     },
21095
21096     unregisterNode : function(node){
21097         delete this.nodeHash[node.id];
21098     },
21099
21100     toString : function(){
21101         return "[Tree"+(this.id?" "+this.id:"")+"]";
21102     }
21103 });
21104
21105 /**
21106  * @class Roo.data.Node
21107  * @extends Roo.util.Observable
21108  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21109  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21110  * @constructor
21111  * @param {Object} attributes The attributes/config for the node
21112  */
21113 Roo.data.Node = function(attributes){
21114     /**
21115      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21116      * @type {Object}
21117      */
21118     this.attributes = attributes || {};
21119     this.leaf = this.attributes.leaf;
21120     /**
21121      * The node id. @type String
21122      */
21123     this.id = this.attributes.id;
21124     if(!this.id){
21125         this.id = Roo.id(null, "ynode-");
21126         this.attributes.id = this.id;
21127     }
21128     /**
21129      * All child nodes of this node. @type Array
21130      */
21131     this.childNodes = [];
21132     if(!this.childNodes.indexOf){ // indexOf is a must
21133         this.childNodes.indexOf = function(o){
21134             for(var i = 0, len = this.length; i < len; i++){
21135                 if(this[i] == o) {
21136                     return i;
21137                 }
21138             }
21139             return -1;
21140         };
21141     }
21142     /**
21143      * The parent node for this node. @type Node
21144      */
21145     this.parentNode = null;
21146     /**
21147      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21148      */
21149     this.firstChild = null;
21150     /**
21151      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21152      */
21153     this.lastChild = null;
21154     /**
21155      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21156      */
21157     this.previousSibling = null;
21158     /**
21159      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21160      */
21161     this.nextSibling = null;
21162
21163     this.addEvents({
21164        /**
21165         * @event append
21166         * Fires when a new child node is appended
21167         * @param {Tree} tree The owner tree
21168         * @param {Node} this This node
21169         * @param {Node} node The newly appended node
21170         * @param {Number} index The index of the newly appended node
21171         */
21172        "append" : true,
21173        /**
21174         * @event remove
21175         * Fires when a child node is removed
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The removed node
21179         */
21180        "remove" : true,
21181        /**
21182         * @event move
21183         * Fires when this node is moved to a new location in the tree
21184         * @param {Tree} tree The owner tree
21185         * @param {Node} this This node
21186         * @param {Node} oldParent The old parent of this node
21187         * @param {Node} newParent The new parent of this node
21188         * @param {Number} index The index it was moved to
21189         */
21190        "move" : true,
21191        /**
21192         * @event insert
21193         * Fires when a new child node is inserted.
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The child node inserted
21197         * @param {Node} refNode The child node the node was inserted before
21198         */
21199        "insert" : true,
21200        /**
21201         * @event beforeappend
21202         * Fires before a new child is appended, return false to cancel the append.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node to be appended
21206         */
21207        "beforeappend" : true,
21208        /**
21209         * @event beforeremove
21210         * Fires before a child is removed, return false to cancel the remove.
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} node The child node to be removed
21214         */
21215        "beforeremove" : true,
21216        /**
21217         * @event beforemove
21218         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21219         * @param {Tree} tree The owner tree
21220         * @param {Node} this This node
21221         * @param {Node} oldParent The parent of this node
21222         * @param {Node} newParent The new parent this node is moving to
21223         * @param {Number} index The index it is being moved to
21224         */
21225        "beforemove" : true,
21226        /**
21227         * @event beforeinsert
21228         * Fires before a new child is inserted, return false to cancel the insert.
21229         * @param {Tree} tree The owner tree
21230         * @param {Node} this This node
21231         * @param {Node} node The child node to be inserted
21232         * @param {Node} refNode The child node the node is being inserted before
21233         */
21234        "beforeinsert" : true
21235    });
21236     this.listeners = this.attributes.listeners;
21237     Roo.data.Node.superclass.constructor.call(this);
21238 };
21239
21240 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21241     fireEvent : function(evtName){
21242         // first do standard event for this node
21243         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21244             return false;
21245         }
21246         // then bubble it up to the tree if the event wasn't cancelled
21247         var ot = this.getOwnerTree();
21248         if(ot){
21249             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21250                 return false;
21251             }
21252         }
21253         return true;
21254     },
21255
21256     /**
21257      * Returns true if this node is a leaf
21258      * @return {Boolean}
21259      */
21260     isLeaf : function(){
21261         return this.leaf === true;
21262     },
21263
21264     // private
21265     setFirstChild : function(node){
21266         this.firstChild = node;
21267     },
21268
21269     //private
21270     setLastChild : function(node){
21271         this.lastChild = node;
21272     },
21273
21274
21275     /**
21276      * Returns true if this node is the last child of its parent
21277      * @return {Boolean}
21278      */
21279     isLast : function(){
21280        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21281     },
21282
21283     /**
21284      * Returns true if this node is the first child of its parent
21285      * @return {Boolean}
21286      */
21287     isFirst : function(){
21288        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21289     },
21290
21291     hasChildNodes : function(){
21292         return !this.isLeaf() && this.childNodes.length > 0;
21293     },
21294
21295     /**
21296      * Insert node(s) as the last child node of this node.
21297      * @param {Node/Array} node The node or Array of nodes to append
21298      * @return {Node} The appended node if single append, or null if an array was passed
21299      */
21300     appendChild : function(node){
21301         var multi = false;
21302         if(node instanceof Array){
21303             multi = node;
21304         }else if(arguments.length > 1){
21305             multi = arguments;
21306         }
21307         // if passed an array or multiple args do them one by one
21308         if(multi){
21309             for(var i = 0, len = multi.length; i < len; i++) {
21310                 this.appendChild(multi[i]);
21311             }
21312         }else{
21313             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21314                 return false;
21315             }
21316             var index = this.childNodes.length;
21317             var oldParent = node.parentNode;
21318             // it's a move, make sure we move it cleanly
21319             if(oldParent){
21320                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21321                     return false;
21322                 }
21323                 oldParent.removeChild(node);
21324             }
21325             index = this.childNodes.length;
21326             if(index == 0){
21327                 this.setFirstChild(node);
21328             }
21329             this.childNodes.push(node);
21330             node.parentNode = this;
21331             var ps = this.childNodes[index-1];
21332             if(ps){
21333                 node.previousSibling = ps;
21334                 ps.nextSibling = node;
21335             }else{
21336                 node.previousSibling = null;
21337             }
21338             node.nextSibling = null;
21339             this.setLastChild(node);
21340             node.setOwnerTree(this.getOwnerTree());
21341             this.fireEvent("append", this.ownerTree, this, node, index);
21342             if(oldParent){
21343                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21344             }
21345             return node;
21346         }
21347     },
21348
21349     /**
21350      * Removes a child node from this node.
21351      * @param {Node} node The node to remove
21352      * @return {Node} The removed node
21353      */
21354     removeChild : function(node){
21355         var index = this.childNodes.indexOf(node);
21356         if(index == -1){
21357             return false;
21358         }
21359         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21360             return false;
21361         }
21362
21363         // remove it from childNodes collection
21364         this.childNodes.splice(index, 1);
21365
21366         // update siblings
21367         if(node.previousSibling){
21368             node.previousSibling.nextSibling = node.nextSibling;
21369         }
21370         if(node.nextSibling){
21371             node.nextSibling.previousSibling = node.previousSibling;
21372         }
21373
21374         // update child refs
21375         if(this.firstChild == node){
21376             this.setFirstChild(node.nextSibling);
21377         }
21378         if(this.lastChild == node){
21379             this.setLastChild(node.previousSibling);
21380         }
21381
21382         node.setOwnerTree(null);
21383         // clear any references from the node
21384         node.parentNode = null;
21385         node.previousSibling = null;
21386         node.nextSibling = null;
21387         this.fireEvent("remove", this.ownerTree, this, node);
21388         return node;
21389     },
21390
21391     /**
21392      * Inserts the first node before the second node in this nodes childNodes collection.
21393      * @param {Node} node The node to insert
21394      * @param {Node} refNode The node to insert before (if null the node is appended)
21395      * @return {Node} The inserted node
21396      */
21397     insertBefore : function(node, refNode){
21398         if(!refNode){ // like standard Dom, refNode can be null for append
21399             return this.appendChild(node);
21400         }
21401         // nothing to do
21402         if(node == refNode){
21403             return false;
21404         }
21405
21406         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21407             return false;
21408         }
21409         var index = this.childNodes.indexOf(refNode);
21410         var oldParent = node.parentNode;
21411         var refIndex = index;
21412
21413         // when moving internally, indexes will change after remove
21414         if(oldParent == this && this.childNodes.indexOf(node) < index){
21415             refIndex--;
21416         }
21417
21418         // it's a move, make sure we move it cleanly
21419         if(oldParent){
21420             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21421                 return false;
21422             }
21423             oldParent.removeChild(node);
21424         }
21425         if(refIndex == 0){
21426             this.setFirstChild(node);
21427         }
21428         this.childNodes.splice(refIndex, 0, node);
21429         node.parentNode = this;
21430         var ps = this.childNodes[refIndex-1];
21431         if(ps){
21432             node.previousSibling = ps;
21433             ps.nextSibling = node;
21434         }else{
21435             node.previousSibling = null;
21436         }
21437         node.nextSibling = refNode;
21438         refNode.previousSibling = node;
21439         node.setOwnerTree(this.getOwnerTree());
21440         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21441         if(oldParent){
21442             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21443         }
21444         return node;
21445     },
21446
21447     /**
21448      * Returns the child node at the specified index.
21449      * @param {Number} index
21450      * @return {Node}
21451      */
21452     item : function(index){
21453         return this.childNodes[index];
21454     },
21455
21456     /**
21457      * Replaces one child node in this node with another.
21458      * @param {Node} newChild The replacement node
21459      * @param {Node} oldChild The node to replace
21460      * @return {Node} The replaced node
21461      */
21462     replaceChild : function(newChild, oldChild){
21463         this.insertBefore(newChild, oldChild);
21464         this.removeChild(oldChild);
21465         return oldChild;
21466     },
21467
21468     /**
21469      * Returns the index of a child node
21470      * @param {Node} node
21471      * @return {Number} The index of the node or -1 if it was not found
21472      */
21473     indexOf : function(child){
21474         return this.childNodes.indexOf(child);
21475     },
21476
21477     /**
21478      * Returns the tree this node is in.
21479      * @return {Tree}
21480      */
21481     getOwnerTree : function(){
21482         // if it doesn't have one, look for one
21483         if(!this.ownerTree){
21484             var p = this;
21485             while(p){
21486                 if(p.ownerTree){
21487                     this.ownerTree = p.ownerTree;
21488                     break;
21489                 }
21490                 p = p.parentNode;
21491             }
21492         }
21493         return this.ownerTree;
21494     },
21495
21496     /**
21497      * Returns depth of this node (the root node has a depth of 0)
21498      * @return {Number}
21499      */
21500     getDepth : function(){
21501         var depth = 0;
21502         var p = this;
21503         while(p.parentNode){
21504             ++depth;
21505             p = p.parentNode;
21506         }
21507         return depth;
21508     },
21509
21510     // private
21511     setOwnerTree : function(tree){
21512         // if it's move, we need to update everyone
21513         if(tree != this.ownerTree){
21514             if(this.ownerTree){
21515                 this.ownerTree.unregisterNode(this);
21516             }
21517             this.ownerTree = tree;
21518             var cs = this.childNodes;
21519             for(var i = 0, len = cs.length; i < len; i++) {
21520                 cs[i].setOwnerTree(tree);
21521             }
21522             if(tree){
21523                 tree.registerNode(this);
21524             }
21525         }
21526     },
21527
21528     /**
21529      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21530      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21531      * @return {String} The path
21532      */
21533     getPath : function(attr){
21534         attr = attr || "id";
21535         var p = this.parentNode;
21536         var b = [this.attributes[attr]];
21537         while(p){
21538             b.unshift(p.attributes[attr]);
21539             p = p.parentNode;
21540         }
21541         var sep = this.getOwnerTree().pathSeparator;
21542         return sep + b.join(sep);
21543     },
21544
21545     /**
21546      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21547      * function call will be the scope provided or the current node. The arguments to the function
21548      * will be the args provided or the current node. If the function returns false at any point,
21549      * the bubble is stopped.
21550      * @param {Function} fn The function to call
21551      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21552      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21553      */
21554     bubble : function(fn, scope, args){
21555         var p = this;
21556         while(p){
21557             if(fn.call(scope || p, args || p) === false){
21558                 break;
21559             }
21560             p = p.parentNode;
21561         }
21562     },
21563
21564     /**
21565      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21566      * function call will be the scope provided or the current node. The arguments to the function
21567      * will be the args provided or the current node. If the function returns false at any point,
21568      * the cascade is stopped on that branch.
21569      * @param {Function} fn The function to call
21570      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21571      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21572      */
21573     cascade : function(fn, scope, args){
21574         if(fn.call(scope || this, args || this) !== false){
21575             var cs = this.childNodes;
21576             for(var i = 0, len = cs.length; i < len; i++) {
21577                 cs[i].cascade(fn, scope, args);
21578             }
21579         }
21580     },
21581
21582     /**
21583      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21584      * function call will be the scope provided or the current node. The arguments to the function
21585      * will be the args provided or the current node. If the function returns false at any point,
21586      * the iteration stops.
21587      * @param {Function} fn The function to call
21588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21590      */
21591     eachChild : function(fn, scope, args){
21592         var cs = this.childNodes;
21593         for(var i = 0, len = cs.length; i < len; i++) {
21594                 if(fn.call(scope || this, args || cs[i]) === false){
21595                     break;
21596                 }
21597         }
21598     },
21599
21600     /**
21601      * Finds the first child that has the attribute with the specified value.
21602      * @param {String} attribute The attribute name
21603      * @param {Mixed} value The value to search for
21604      * @return {Node} The found child or null if none was found
21605      */
21606     findChild : function(attribute, value){
21607         var cs = this.childNodes;
21608         for(var i = 0, len = cs.length; i < len; i++) {
21609                 if(cs[i].attributes[attribute] == value){
21610                     return cs[i];
21611                 }
21612         }
21613         return null;
21614     },
21615
21616     /**
21617      * Finds the first child by a custom function. The child matches if the function passed
21618      * returns true.
21619      * @param {Function} fn
21620      * @param {Object} scope (optional)
21621      * @return {Node} The found child or null if none was found
21622      */
21623     findChildBy : function(fn, scope){
21624         var cs = this.childNodes;
21625         for(var i = 0, len = cs.length; i < len; i++) {
21626                 if(fn.call(scope||cs[i], cs[i]) === true){
21627                     return cs[i];
21628                 }
21629         }
21630         return null;
21631     },
21632
21633     /**
21634      * Sorts this nodes children using the supplied sort function
21635      * @param {Function} fn
21636      * @param {Object} scope (optional)
21637      */
21638     sort : function(fn, scope){
21639         var cs = this.childNodes;
21640         var len = cs.length;
21641         if(len > 0){
21642             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21643             cs.sort(sortFn);
21644             for(var i = 0; i < len; i++){
21645                 var n = cs[i];
21646                 n.previousSibling = cs[i-1];
21647                 n.nextSibling = cs[i+1];
21648                 if(i == 0){
21649                     this.setFirstChild(n);
21650                 }
21651                 if(i == len-1){
21652                     this.setLastChild(n);
21653                 }
21654             }
21655         }
21656     },
21657
21658     /**
21659      * Returns true if this node is an ancestor (at any point) of the passed node.
21660      * @param {Node} node
21661      * @return {Boolean}
21662      */
21663     contains : function(node){
21664         return node.isAncestor(this);
21665     },
21666
21667     /**
21668      * Returns true if the passed node is an ancestor (at any point) of this node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     isAncestor : function(node){
21673         var p = this.parentNode;
21674         while(p){
21675             if(p == node){
21676                 return true;
21677             }
21678             p = p.parentNode;
21679         }
21680         return false;
21681     },
21682
21683     toString : function(){
21684         return "[Node"+(this.id?" "+this.id:"")+"]";
21685     }
21686 });/*
21687  * Based on:
21688  * Ext JS Library 1.1.1
21689  * Copyright(c) 2006-2007, Ext JS, LLC.
21690  *
21691  * Originally Released Under LGPL - original licence link has changed is not relivant.
21692  *
21693  * Fork - LGPL
21694  * <script type="text/javascript">
21695  */
21696  
21697
21698 /**
21699  * @class Roo.ComponentMgr
21700  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21701  * @singleton
21702  */
21703 Roo.ComponentMgr = function(){
21704     var all = new Roo.util.MixedCollection();
21705
21706     return {
21707         /**
21708          * Registers a component.
21709          * @param {Roo.Component} c The component
21710          */
21711         register : function(c){
21712             all.add(c);
21713         },
21714
21715         /**
21716          * Unregisters a component.
21717          * @param {Roo.Component} c The component
21718          */
21719         unregister : function(c){
21720             all.remove(c);
21721         },
21722
21723         /**
21724          * Returns a component by id
21725          * @param {String} id The component id
21726          */
21727         get : function(id){
21728             return all.get(id);
21729         },
21730
21731         /**
21732          * Registers a function that will be called when a specified component is added to ComponentMgr
21733          * @param {String} id The component id
21734          * @param {Funtction} fn The callback function
21735          * @param {Object} scope The scope of the callback
21736          */
21737         onAvailable : function(id, fn, scope){
21738             all.on("add", function(index, o){
21739                 if(o.id == id){
21740                     fn.call(scope || o, o);
21741                     all.un("add", fn, scope);
21742                 }
21743             });
21744         }
21745     };
21746 }();/*
21747  * Based on:
21748  * Ext JS Library 1.1.1
21749  * Copyright(c) 2006-2007, Ext JS, LLC.
21750  *
21751  * Originally Released Under LGPL - original licence link has changed is not relivant.
21752  *
21753  * Fork - LGPL
21754  * <script type="text/javascript">
21755  */
21756  
21757 /**
21758  * @class Roo.Component
21759  * @extends Roo.util.Observable
21760  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21761  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21762  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21763  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21764  * All visual components (widgets) that require rendering into a layout should subclass Component.
21765  * @constructor
21766  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21767  * 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
21768  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21769  */
21770 Roo.Component = function(config){
21771     config = config || {};
21772     if(config.tagName || config.dom || typeof config == "string"){ // element object
21773         config = {el: config, id: config.id || config};
21774     }
21775     this.initialConfig = config;
21776
21777     Roo.apply(this, config);
21778     this.addEvents({
21779         /**
21780          * @event disable
21781          * Fires after the component is disabled.
21782              * @param {Roo.Component} this
21783              */
21784         disable : true,
21785         /**
21786          * @event enable
21787          * Fires after the component is enabled.
21788              * @param {Roo.Component} this
21789              */
21790         enable : true,
21791         /**
21792          * @event beforeshow
21793          * Fires before the component is shown.  Return false to stop the show.
21794              * @param {Roo.Component} this
21795              */
21796         beforeshow : true,
21797         /**
21798          * @event show
21799          * Fires after the component is shown.
21800              * @param {Roo.Component} this
21801              */
21802         show : true,
21803         /**
21804          * @event beforehide
21805          * Fires before the component is hidden. Return false to stop the hide.
21806              * @param {Roo.Component} this
21807              */
21808         beforehide : true,
21809         /**
21810          * @event hide
21811          * Fires after the component is hidden.
21812              * @param {Roo.Component} this
21813              */
21814         hide : true,
21815         /**
21816          * @event beforerender
21817          * Fires before the component is rendered. Return false to stop the render.
21818              * @param {Roo.Component} this
21819              */
21820         beforerender : true,
21821         /**
21822          * @event render
21823          * Fires after the component is rendered.
21824              * @param {Roo.Component} this
21825              */
21826         render : true,
21827         /**
21828          * @event beforedestroy
21829          * Fires before the component is destroyed. Return false to stop the destroy.
21830              * @param {Roo.Component} this
21831              */
21832         beforedestroy : true,
21833         /**
21834          * @event destroy
21835          * Fires after the component is destroyed.
21836              * @param {Roo.Component} this
21837              */
21838         destroy : true
21839     });
21840     if(!this.id){
21841         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21842     }
21843     Roo.ComponentMgr.register(this);
21844     Roo.Component.superclass.constructor.call(this);
21845     this.initComponent();
21846     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21847         this.render(this.renderTo);
21848         delete this.renderTo;
21849     }
21850 };
21851
21852 // private
21853 Roo.Component.AUTO_ID = 1000;
21854
21855 Roo.extend(Roo.Component, Roo.util.Observable, {
21856     /**
21857      * @property {Boolean} hidden
21858      * true if this component is hidden. Read-only.
21859      */
21860     hidden : false,
21861     /**
21862      * true if this component is disabled. Read-only.
21863      */
21864     disabled : false,
21865     /**
21866      * true if this component has been rendered. Read-only.
21867      */
21868     rendered : false,
21869     
21870     /** @cfg {String} disableClass
21871      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21872      */
21873     disabledClass : "x-item-disabled",
21874         /** @cfg {Boolean} allowDomMove
21875          * Whether the component can move the Dom node when rendering (defaults to true).
21876          */
21877     allowDomMove : true,
21878     /** @cfg {String} hideMode
21879      * How this component should hidden. Supported values are
21880      * "visibility" (css visibility), "offsets" (negative offset position) and
21881      * "display" (css display) - defaults to "display".
21882      */
21883     hideMode: 'display',
21884
21885     // private
21886     ctype : "Roo.Component",
21887
21888     /** @cfg {String} actionMode 
21889      * which property holds the element that used for  hide() / show() / disable() / enable()
21890      * default is 'el' 
21891      */
21892     actionMode : "el",
21893
21894     // private
21895     getActionEl : function(){
21896         return this[this.actionMode];
21897     },
21898
21899     initComponent : Roo.emptyFn,
21900     /**
21901      * If this is a lazy rendering component, render it to its container element.
21902      * @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.
21903      */
21904     render : function(container, position){
21905         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21906             if(!container && this.el){
21907                 this.el = Roo.get(this.el);
21908                 container = this.el.dom.parentNode;
21909                 this.allowDomMove = false;
21910             }
21911             this.container = Roo.get(container);
21912             this.rendered = true;
21913             if(position !== undefined){
21914                 if(typeof position == 'number'){
21915                     position = this.container.dom.childNodes[position];
21916                 }else{
21917                     position = Roo.getDom(position);
21918                 }
21919             }
21920             this.onRender(this.container, position || null);
21921             if(this.cls){
21922                 this.el.addClass(this.cls);
21923                 delete this.cls;
21924             }
21925             if(this.style){
21926                 this.el.applyStyles(this.style);
21927                 delete this.style;
21928             }
21929             this.fireEvent("render", this);
21930             this.afterRender(this.container);
21931             if(this.hidden){
21932                 this.hide();
21933             }
21934             if(this.disabled){
21935                 this.disable();
21936             }
21937         }
21938         return this;
21939     },
21940
21941     // private
21942     // default function is not really useful
21943     onRender : function(ct, position){
21944         if(this.el){
21945             this.el = Roo.get(this.el);
21946             if(this.allowDomMove !== false){
21947                 ct.dom.insertBefore(this.el.dom, position);
21948             }
21949         }
21950     },
21951
21952     // private
21953     getAutoCreate : function(){
21954         var cfg = typeof this.autoCreate == "object" ?
21955                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21956         if(this.id && !cfg.id){
21957             cfg.id = this.id;
21958         }
21959         return cfg;
21960     },
21961
21962     // private
21963     afterRender : Roo.emptyFn,
21964
21965     /**
21966      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21967      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21968      */
21969     destroy : function(){
21970         if(this.fireEvent("beforedestroy", this) !== false){
21971             this.purgeListeners();
21972             this.beforeDestroy();
21973             if(this.rendered){
21974                 this.el.removeAllListeners();
21975                 this.el.remove();
21976                 if(this.actionMode == "container"){
21977                     this.container.remove();
21978                 }
21979             }
21980             this.onDestroy();
21981             Roo.ComponentMgr.unregister(this);
21982             this.fireEvent("destroy", this);
21983         }
21984     },
21985
21986         // private
21987     beforeDestroy : function(){
21988
21989     },
21990
21991         // private
21992         onDestroy : function(){
21993
21994     },
21995
21996     /**
21997      * Returns the underlying {@link Roo.Element}.
21998      * @return {Roo.Element} The element
21999      */
22000     getEl : function(){
22001         return this.el;
22002     },
22003
22004     /**
22005      * Returns the id of this component.
22006      * @return {String}
22007      */
22008     getId : function(){
22009         return this.id;
22010     },
22011
22012     /**
22013      * Try to focus this component.
22014      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22015      * @return {Roo.Component} this
22016      */
22017     focus : function(selectText){
22018         if(this.rendered){
22019             this.el.focus();
22020             if(selectText === true){
22021                 this.el.dom.select();
22022             }
22023         }
22024         return this;
22025     },
22026
22027     // private
22028     blur : function(){
22029         if(this.rendered){
22030             this.el.blur();
22031         }
22032         return this;
22033     },
22034
22035     /**
22036      * Disable this component.
22037      * @return {Roo.Component} this
22038      */
22039     disable : function(){
22040         if(this.rendered){
22041             this.onDisable();
22042         }
22043         this.disabled = true;
22044         this.fireEvent("disable", this);
22045         return this;
22046     },
22047
22048         // private
22049     onDisable : function(){
22050         this.getActionEl().addClass(this.disabledClass);
22051         this.el.dom.disabled = true;
22052     },
22053
22054     /**
22055      * Enable this component.
22056      * @return {Roo.Component} this
22057      */
22058     enable : function(){
22059         if(this.rendered){
22060             this.onEnable();
22061         }
22062         this.disabled = false;
22063         this.fireEvent("enable", this);
22064         return this;
22065     },
22066
22067         // private
22068     onEnable : function(){
22069         this.getActionEl().removeClass(this.disabledClass);
22070         this.el.dom.disabled = false;
22071     },
22072
22073     /**
22074      * Convenience function for setting disabled/enabled by boolean.
22075      * @param {Boolean} disabled
22076      */
22077     setDisabled : function(disabled){
22078         this[disabled ? "disable" : "enable"]();
22079     },
22080
22081     /**
22082      * Show this component.
22083      * @return {Roo.Component} this
22084      */
22085     show: function(){
22086         if(this.fireEvent("beforeshow", this) !== false){
22087             this.hidden = false;
22088             if(this.rendered){
22089                 this.onShow();
22090             }
22091             this.fireEvent("show", this);
22092         }
22093         return this;
22094     },
22095
22096     // private
22097     onShow : function(){
22098         var ae = this.getActionEl();
22099         if(this.hideMode == 'visibility'){
22100             ae.dom.style.visibility = "visible";
22101         }else if(this.hideMode == 'offsets'){
22102             ae.removeClass('x-hidden');
22103         }else{
22104             ae.dom.style.display = "";
22105         }
22106     },
22107
22108     /**
22109      * Hide this component.
22110      * @return {Roo.Component} this
22111      */
22112     hide: function(){
22113         if(this.fireEvent("beforehide", this) !== false){
22114             this.hidden = true;
22115             if(this.rendered){
22116                 this.onHide();
22117             }
22118             this.fireEvent("hide", this);
22119         }
22120         return this;
22121     },
22122
22123     // private
22124     onHide : function(){
22125         var ae = this.getActionEl();
22126         if(this.hideMode == 'visibility'){
22127             ae.dom.style.visibility = "hidden";
22128         }else if(this.hideMode == 'offsets'){
22129             ae.addClass('x-hidden');
22130         }else{
22131             ae.dom.style.display = "none";
22132         }
22133     },
22134
22135     /**
22136      * Convenience function to hide or show this component by boolean.
22137      * @param {Boolean} visible True to show, false to hide
22138      * @return {Roo.Component} this
22139      */
22140     setVisible: function(visible){
22141         if(visible) {
22142             this.show();
22143         }else{
22144             this.hide();
22145         }
22146         return this;
22147     },
22148
22149     /**
22150      * Returns true if this component is visible.
22151      */
22152     isVisible : function(){
22153         return this.getActionEl().isVisible();
22154     },
22155
22156     cloneConfig : function(overrides){
22157         overrides = overrides || {};
22158         var id = overrides.id || Roo.id();
22159         var cfg = Roo.applyIf(overrides, this.initialConfig);
22160         cfg.id = id; // prevent dup id
22161         return new this.constructor(cfg);
22162     }
22163 });/*
22164  * Based on:
22165  * Ext JS Library 1.1.1
22166  * Copyright(c) 2006-2007, Ext JS, LLC.
22167  *
22168  * Originally Released Under LGPL - original licence link has changed is not relivant.
22169  *
22170  * Fork - LGPL
22171  * <script type="text/javascript">
22172  */
22173  (function(){ 
22174 /**
22175  * @class Roo.Layer
22176  * @extends Roo.Element
22177  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22178  * automatic maintaining of shadow/shim positions.
22179  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22180  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22181  * you can pass a string with a CSS class name. False turns off the shadow.
22182  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22183  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22184  * @cfg {String} cls CSS class to add to the element
22185  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22186  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22187  * @constructor
22188  * @param {Object} config An object with config options.
22189  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22190  */
22191
22192 Roo.Layer = function(config, existingEl){
22193     config = config || {};
22194     var dh = Roo.DomHelper;
22195     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22196     if(existingEl){
22197         this.dom = Roo.getDom(existingEl);
22198     }
22199     if(!this.dom){
22200         var o = config.dh || {tag: "div", cls: "x-layer"};
22201         this.dom = dh.append(pel, o);
22202     }
22203     if(config.cls){
22204         this.addClass(config.cls);
22205     }
22206     this.constrain = config.constrain !== false;
22207     this.visibilityMode = Roo.Element.VISIBILITY;
22208     if(config.id){
22209         this.id = this.dom.id = config.id;
22210     }else{
22211         this.id = Roo.id(this.dom);
22212     }
22213     this.zindex = config.zindex || this.getZIndex();
22214     this.position("absolute", this.zindex);
22215     if(config.shadow){
22216         this.shadowOffset = config.shadowOffset || 4;
22217         this.shadow = new Roo.Shadow({
22218             offset : this.shadowOffset,
22219             mode : config.shadow
22220         });
22221     }else{
22222         this.shadowOffset = 0;
22223     }
22224     this.useShim = config.shim !== false && Roo.useShims;
22225     this.useDisplay = config.useDisplay;
22226     this.hide();
22227 };
22228
22229 var supr = Roo.Element.prototype;
22230
22231 // shims are shared among layer to keep from having 100 iframes
22232 var shims = [];
22233
22234 Roo.extend(Roo.Layer, Roo.Element, {
22235
22236     getZIndex : function(){
22237         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22238     },
22239
22240     getShim : function(){
22241         if(!this.useShim){
22242             return null;
22243         }
22244         if(this.shim){
22245             return this.shim;
22246         }
22247         var shim = shims.shift();
22248         if(!shim){
22249             shim = this.createShim();
22250             shim.enableDisplayMode('block');
22251             shim.dom.style.display = 'none';
22252             shim.dom.style.visibility = 'visible';
22253         }
22254         var pn = this.dom.parentNode;
22255         if(shim.dom.parentNode != pn){
22256             pn.insertBefore(shim.dom, this.dom);
22257         }
22258         shim.setStyle('z-index', this.getZIndex()-2);
22259         this.shim = shim;
22260         return shim;
22261     },
22262
22263     hideShim : function(){
22264         if(this.shim){
22265             this.shim.setDisplayed(false);
22266             shims.push(this.shim);
22267             delete this.shim;
22268         }
22269     },
22270
22271     disableShadow : function(){
22272         if(this.shadow){
22273             this.shadowDisabled = true;
22274             this.shadow.hide();
22275             this.lastShadowOffset = this.shadowOffset;
22276             this.shadowOffset = 0;
22277         }
22278     },
22279
22280     enableShadow : function(show){
22281         if(this.shadow){
22282             this.shadowDisabled = false;
22283             this.shadowOffset = this.lastShadowOffset;
22284             delete this.lastShadowOffset;
22285             if(show){
22286                 this.sync(true);
22287             }
22288         }
22289     },
22290
22291     // private
22292     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22293     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22294     sync : function(doShow){
22295         var sw = this.shadow;
22296         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22297             var sh = this.getShim();
22298
22299             var w = this.getWidth(),
22300                 h = this.getHeight();
22301
22302             var l = this.getLeft(true),
22303                 t = this.getTop(true);
22304
22305             if(sw && !this.shadowDisabled){
22306                 if(doShow && !sw.isVisible()){
22307                     sw.show(this);
22308                 }else{
22309                     sw.realign(l, t, w, h);
22310                 }
22311                 if(sh){
22312                     if(doShow){
22313                        sh.show();
22314                     }
22315                     // fit the shim behind the shadow, so it is shimmed too
22316                     var a = sw.adjusts, s = sh.dom.style;
22317                     s.left = (Math.min(l, l+a.l))+"px";
22318                     s.top = (Math.min(t, t+a.t))+"px";
22319                     s.width = (w+a.w)+"px";
22320                     s.height = (h+a.h)+"px";
22321                 }
22322             }else if(sh){
22323                 if(doShow){
22324                    sh.show();
22325                 }
22326                 sh.setSize(w, h);
22327                 sh.setLeftTop(l, t);
22328             }
22329             
22330         }
22331     },
22332
22333     // private
22334     destroy : function(){
22335         this.hideShim();
22336         if(this.shadow){
22337             this.shadow.hide();
22338         }
22339         this.removeAllListeners();
22340         var pn = this.dom.parentNode;
22341         if(pn){
22342             pn.removeChild(this.dom);
22343         }
22344         Roo.Element.uncache(this.id);
22345     },
22346
22347     remove : function(){
22348         this.destroy();
22349     },
22350
22351     // private
22352     beginUpdate : function(){
22353         this.updating = true;
22354     },
22355
22356     // private
22357     endUpdate : function(){
22358         this.updating = false;
22359         this.sync(true);
22360     },
22361
22362     // private
22363     hideUnders : function(negOffset){
22364         if(this.shadow){
22365             this.shadow.hide();
22366         }
22367         this.hideShim();
22368     },
22369
22370     // private
22371     constrainXY : function(){
22372         if(this.constrain){
22373             var vw = Roo.lib.Dom.getViewWidth(),
22374                 vh = Roo.lib.Dom.getViewHeight();
22375             var s = Roo.get(document).getScroll();
22376
22377             var xy = this.getXY();
22378             var x = xy[0], y = xy[1];   
22379             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22380             // only move it if it needs it
22381             var moved = false;
22382             // first validate right/bottom
22383             if((x + w) > vw+s.left){
22384                 x = vw - w - this.shadowOffset;
22385                 moved = true;
22386             }
22387             if((y + h) > vh+s.top){
22388                 y = vh - h - this.shadowOffset;
22389                 moved = true;
22390             }
22391             // then make sure top/left isn't negative
22392             if(x < s.left){
22393                 x = s.left;
22394                 moved = true;
22395             }
22396             if(y < s.top){
22397                 y = s.top;
22398                 moved = true;
22399             }
22400             if(moved){
22401                 if(this.avoidY){
22402                     var ay = this.avoidY;
22403                     if(y <= ay && (y+h) >= ay){
22404                         y = ay-h-5;   
22405                     }
22406                 }
22407                 xy = [x, y];
22408                 this.storeXY(xy);
22409                 supr.setXY.call(this, xy);
22410                 this.sync();
22411             }
22412         }
22413     },
22414
22415     isVisible : function(){
22416         return this.visible;    
22417     },
22418
22419     // private
22420     showAction : function(){
22421         this.visible = true; // track visibility to prevent getStyle calls
22422         if(this.useDisplay === true){
22423             this.setDisplayed("");
22424         }else if(this.lastXY){
22425             supr.setXY.call(this, this.lastXY);
22426         }else if(this.lastLT){
22427             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22428         }
22429     },
22430
22431     // private
22432     hideAction : function(){
22433         this.visible = false;
22434         if(this.useDisplay === true){
22435             this.setDisplayed(false);
22436         }else{
22437             this.setLeftTop(-10000,-10000);
22438         }
22439     },
22440
22441     // overridden Element method
22442     setVisible : function(v, a, d, c, e){
22443         if(v){
22444             this.showAction();
22445         }
22446         if(a && v){
22447             var cb = function(){
22448                 this.sync(true);
22449                 if(c){
22450                     c();
22451                 }
22452             }.createDelegate(this);
22453             supr.setVisible.call(this, true, true, d, cb, e);
22454         }else{
22455             if(!v){
22456                 this.hideUnders(true);
22457             }
22458             var cb = c;
22459             if(a){
22460                 cb = function(){
22461                     this.hideAction();
22462                     if(c){
22463                         c();
22464                     }
22465                 }.createDelegate(this);
22466             }
22467             supr.setVisible.call(this, v, a, d, cb, e);
22468             if(v){
22469                 this.sync(true);
22470             }else if(!a){
22471                 this.hideAction();
22472             }
22473         }
22474     },
22475
22476     storeXY : function(xy){
22477         delete this.lastLT;
22478         this.lastXY = xy;
22479     },
22480
22481     storeLeftTop : function(left, top){
22482         delete this.lastXY;
22483         this.lastLT = [left, top];
22484     },
22485
22486     // private
22487     beforeFx : function(){
22488         this.beforeAction();
22489         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22490     },
22491
22492     // private
22493     afterFx : function(){
22494         Roo.Layer.superclass.afterFx.apply(this, arguments);
22495         this.sync(this.isVisible());
22496     },
22497
22498     // private
22499     beforeAction : function(){
22500         if(!this.updating && this.shadow){
22501             this.shadow.hide();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setLeft : function(left){
22507         this.storeLeftTop(left, this.getTop(true));
22508         supr.setLeft.apply(this, arguments);
22509         this.sync();
22510     },
22511
22512     setTop : function(top){
22513         this.storeLeftTop(this.getLeft(true), top);
22514         supr.setTop.apply(this, arguments);
22515         this.sync();
22516     },
22517
22518     setLeftTop : function(left, top){
22519         this.storeLeftTop(left, top);
22520         supr.setLeftTop.apply(this, arguments);
22521         this.sync();
22522     },
22523
22524     setXY : function(xy, a, d, c, e){
22525         this.fixDisplay();
22526         this.beforeAction();
22527         this.storeXY(xy);
22528         var cb = this.createCB(c);
22529         supr.setXY.call(this, xy, a, d, cb, e);
22530         if(!a){
22531             cb();
22532         }
22533     },
22534
22535     // private
22536     createCB : function(c){
22537         var el = this;
22538         return function(){
22539             el.constrainXY();
22540             el.sync(true);
22541             if(c){
22542                 c();
22543             }
22544         };
22545     },
22546
22547     // overridden Element method
22548     setX : function(x, a, d, c, e){
22549         this.setXY([x, this.getY()], a, d, c, e);
22550     },
22551
22552     // overridden Element method
22553     setY : function(y, a, d, c, e){
22554         this.setXY([this.getX(), y], a, d, c, e);
22555     },
22556
22557     // overridden Element method
22558     setSize : function(w, h, a, d, c, e){
22559         this.beforeAction();
22560         var cb = this.createCB(c);
22561         supr.setSize.call(this, w, h, a, d, cb, e);
22562         if(!a){
22563             cb();
22564         }
22565     },
22566
22567     // overridden Element method
22568     setWidth : function(w, a, d, c, e){
22569         this.beforeAction();
22570         var cb = this.createCB(c);
22571         supr.setWidth.call(this, w, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // overridden Element method
22578     setHeight : function(h, a, d, c, e){
22579         this.beforeAction();
22580         var cb = this.createCB(c);
22581         supr.setHeight.call(this, h, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // overridden Element method
22588     setBounds : function(x, y, w, h, a, d, c, e){
22589         this.beforeAction();
22590         var cb = this.createCB(c);
22591         if(!a){
22592             this.storeXY([x, y]);
22593             supr.setXY.call(this, [x, y]);
22594             supr.setSize.call(this, w, h, a, d, cb, e);
22595             cb();
22596         }else{
22597             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22598         }
22599         return this;
22600     },
22601     
22602     /**
22603      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22604      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22605      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22606      * @param {Number} zindex The new z-index to set
22607      * @return {this} The Layer
22608      */
22609     setZIndex : function(zindex){
22610         this.zindex = zindex;
22611         this.setStyle("z-index", zindex + 2);
22612         if(this.shadow){
22613             this.shadow.setZIndex(zindex + 1);
22614         }
22615         if(this.shim){
22616             this.shim.setStyle("z-index", zindex);
22617         }
22618     }
22619 });
22620 })();/*
22621  * Based on:
22622  * Ext JS Library 1.1.1
22623  * Copyright(c) 2006-2007, Ext JS, LLC.
22624  *
22625  * Originally Released Under LGPL - original licence link has changed is not relivant.
22626  *
22627  * Fork - LGPL
22628  * <script type="text/javascript">
22629  */
22630
22631
22632 /**
22633  * @class Roo.Shadow
22634  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22635  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22636  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22637  * @constructor
22638  * Create a new Shadow
22639  * @param {Object} config The config object
22640  */
22641 Roo.Shadow = function(config){
22642     Roo.apply(this, config);
22643     if(typeof this.mode != "string"){
22644         this.mode = this.defaultMode;
22645     }
22646     var o = this.offset, a = {h: 0};
22647     var rad = Math.floor(this.offset/2);
22648     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22649         case "drop":
22650             a.w = 0;
22651             a.l = a.t = o;
22652             a.t -= 1;
22653             if(Roo.isIE){
22654                 a.l -= this.offset + rad;
22655                 a.t -= this.offset + rad;
22656                 a.w -= rad;
22657                 a.h -= rad;
22658                 a.t += 1;
22659             }
22660         break;
22661         case "sides":
22662             a.w = (o*2);
22663             a.l = -o;
22664             a.t = o-1;
22665             if(Roo.isIE){
22666                 a.l -= (this.offset - rad);
22667                 a.t -= this.offset + rad;
22668                 a.l += 1;
22669                 a.w -= (this.offset - rad)*2;
22670                 a.w -= rad + 1;
22671                 a.h -= 1;
22672             }
22673         break;
22674         case "frame":
22675             a.w = a.h = (o*2);
22676             a.l = a.t = -o;
22677             a.t += 1;
22678             a.h -= 2;
22679             if(Roo.isIE){
22680                 a.l -= (this.offset - rad);
22681                 a.t -= (this.offset - rad);
22682                 a.l += 1;
22683                 a.w -= (this.offset + rad + 1);
22684                 a.h -= (this.offset + rad);
22685                 a.h += 1;
22686             }
22687         break;
22688     };
22689
22690     this.adjusts = a;
22691 };
22692
22693 Roo.Shadow.prototype = {
22694     /**
22695      * @cfg {String} mode
22696      * The shadow display mode.  Supports the following options:<br />
22697      * sides: Shadow displays on both sides and bottom only<br />
22698      * frame: Shadow displays equally on all four sides<br />
22699      * drop: Traditional bottom-right drop shadow (default)
22700      */
22701     /**
22702      * @cfg {String} offset
22703      * The number of pixels to offset the shadow from the element (defaults to 4)
22704      */
22705     offset: 4,
22706
22707     // private
22708     defaultMode: "drop",
22709
22710     /**
22711      * Displays the shadow under the target element
22712      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22713      */
22714     show : function(target){
22715         target = Roo.get(target);
22716         if(!this.el){
22717             this.el = Roo.Shadow.Pool.pull();
22718             if(this.el.dom.nextSibling != target.dom){
22719                 this.el.insertBefore(target);
22720             }
22721         }
22722         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22723         if(Roo.isIE){
22724             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22725         }
22726         this.realign(
22727             target.getLeft(true),
22728             target.getTop(true),
22729             target.getWidth(),
22730             target.getHeight()
22731         );
22732         this.el.dom.style.display = "block";
22733     },
22734
22735     /**
22736      * Returns true if the shadow is visible, else false
22737      */
22738     isVisible : function(){
22739         return this.el ? true : false;  
22740     },
22741
22742     /**
22743      * Direct alignment when values are already available. Show must be called at least once before
22744      * calling this method to ensure it is initialized.
22745      * @param {Number} left The target element left position
22746      * @param {Number} top The target element top position
22747      * @param {Number} width The target element width
22748      * @param {Number} height The target element height
22749      */
22750     realign : function(l, t, w, h){
22751         if(!this.el){
22752             return;
22753         }
22754         var a = this.adjusts, d = this.el.dom, s = d.style;
22755         var iea = 0;
22756         s.left = (l+a.l)+"px";
22757         s.top = (t+a.t)+"px";
22758         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22759  
22760         if(s.width != sws || s.height != shs){
22761             s.width = sws;
22762             s.height = shs;
22763             if(!Roo.isIE){
22764                 var cn = d.childNodes;
22765                 var sww = Math.max(0, (sw-12))+"px";
22766                 cn[0].childNodes[1].style.width = sww;
22767                 cn[1].childNodes[1].style.width = sww;
22768                 cn[2].childNodes[1].style.width = sww;
22769                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22770             }
22771         }
22772     },
22773
22774     /**
22775      * Hides this shadow
22776      */
22777     hide : function(){
22778         if(this.el){
22779             this.el.dom.style.display = "none";
22780             Roo.Shadow.Pool.push(this.el);
22781             delete this.el;
22782         }
22783     },
22784
22785     /**
22786      * Adjust the z-index of this shadow
22787      * @param {Number} zindex The new z-index
22788      */
22789     setZIndex : function(z){
22790         this.zIndex = z;
22791         if(this.el){
22792             this.el.setStyle("z-index", z);
22793         }
22794     }
22795 };
22796
22797 // Private utility class that manages the internal Shadow cache
22798 Roo.Shadow.Pool = function(){
22799     var p = [];
22800     var markup = Roo.isIE ?
22801                  '<div class="x-ie-shadow"></div>' :
22802                  '<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>';
22803     return {
22804         pull : function(){
22805             var sh = p.shift();
22806             if(!sh){
22807                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22808                 sh.autoBoxAdjust = false;
22809             }
22810             return sh;
22811         },
22812
22813         push : function(sh){
22814             p.push(sh);
22815         }
22816     };
22817 }();/*
22818  * Based on:
22819  * Ext JS Library 1.1.1
22820  * Copyright(c) 2006-2007, Ext JS, LLC.
22821  *
22822  * Originally Released Under LGPL - original licence link has changed is not relivant.
22823  *
22824  * Fork - LGPL
22825  * <script type="text/javascript">
22826  */
22827
22828 /**
22829  * @class Roo.BoxComponent
22830  * @extends Roo.Component
22831  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22832  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22833  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22834  * layout containers.
22835  * @constructor
22836  * @param {Roo.Element/String/Object} config The configuration options.
22837  */
22838 Roo.BoxComponent = function(config){
22839     Roo.Component.call(this, config);
22840     this.addEvents({
22841         /**
22842          * @event resize
22843          * Fires after the component is resized.
22844              * @param {Roo.Component} this
22845              * @param {Number} adjWidth The box-adjusted width that was set
22846              * @param {Number} adjHeight The box-adjusted height that was set
22847              * @param {Number} rawWidth The width that was originally specified
22848              * @param {Number} rawHeight The height that was originally specified
22849              */
22850         resize : true,
22851         /**
22852          * @event move
22853          * Fires after the component is moved.
22854              * @param {Roo.Component} this
22855              * @param {Number} x The new x position
22856              * @param {Number} y The new y position
22857              */
22858         move : true
22859     });
22860 };
22861
22862 Roo.extend(Roo.BoxComponent, Roo.Component, {
22863     // private, set in afterRender to signify that the component has been rendered
22864     boxReady : false,
22865     // private, used to defer height settings to subclasses
22866     deferHeight: false,
22867     /** @cfg {Number} width
22868      * width (optional) size of component
22869      */
22870      /** @cfg {Number} height
22871      * height (optional) size of component
22872      */
22873      
22874     /**
22875      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22876      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22877      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22878      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22879      * @return {Roo.BoxComponent} this
22880      */
22881     setSize : function(w, h){
22882         // support for standard size objects
22883         if(typeof w == 'object'){
22884             h = w.height;
22885             w = w.width;
22886         }
22887         // not rendered
22888         if(!this.boxReady){
22889             this.width = w;
22890             this.height = h;
22891             return this;
22892         }
22893
22894         // prevent recalcs when not needed
22895         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22896             return this;
22897         }
22898         this.lastSize = {width: w, height: h};
22899
22900         var adj = this.adjustSize(w, h);
22901         var aw = adj.width, ah = adj.height;
22902         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22903             var rz = this.getResizeEl();
22904             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22905                 rz.setSize(aw, ah);
22906             }else if(!this.deferHeight && ah !== undefined){
22907                 rz.setHeight(ah);
22908             }else if(aw !== undefined){
22909                 rz.setWidth(aw);
22910             }
22911             this.onResize(aw, ah, w, h);
22912             this.fireEvent('resize', this, aw, ah, w, h);
22913         }
22914         return this;
22915     },
22916
22917     /**
22918      * Gets the current size of the component's underlying element.
22919      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22920      */
22921     getSize : function(){
22922         return this.el.getSize();
22923     },
22924
22925     /**
22926      * Gets the current XY position of the component's underlying element.
22927      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22928      * @return {Array} The XY position of the element (e.g., [100, 200])
22929      */
22930     getPosition : function(local){
22931         if(local === true){
22932             return [this.el.getLeft(true), this.el.getTop(true)];
22933         }
22934         return this.xy || this.el.getXY();
22935     },
22936
22937     /**
22938      * Gets the current box measurements of the component's underlying element.
22939      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22940      * @returns {Object} box An object in the format {x, y, width, height}
22941      */
22942     getBox : function(local){
22943         var s = this.el.getSize();
22944         if(local){
22945             s.x = this.el.getLeft(true);
22946             s.y = this.el.getTop(true);
22947         }else{
22948             var xy = this.xy || this.el.getXY();
22949             s.x = xy[0];
22950             s.y = xy[1];
22951         }
22952         return s;
22953     },
22954
22955     /**
22956      * Sets the current box measurements of the component's underlying element.
22957      * @param {Object} box An object in the format {x, y, width, height}
22958      * @returns {Roo.BoxComponent} this
22959      */
22960     updateBox : function(box){
22961         this.setSize(box.width, box.height);
22962         this.setPagePosition(box.x, box.y);
22963         return this;
22964     },
22965
22966     // protected
22967     getResizeEl : function(){
22968         return this.resizeEl || this.el;
22969     },
22970
22971     // protected
22972     getPositionEl : function(){
22973         return this.positionEl || this.el;
22974     },
22975
22976     /**
22977      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22978      * This method fires the move event.
22979      * @param {Number} left The new left
22980      * @param {Number} top The new top
22981      * @returns {Roo.BoxComponent} this
22982      */
22983     setPosition : function(x, y){
22984         this.x = x;
22985         this.y = y;
22986         if(!this.boxReady){
22987             return this;
22988         }
22989         var adj = this.adjustPosition(x, y);
22990         var ax = adj.x, ay = adj.y;
22991
22992         var el = this.getPositionEl();
22993         if(ax !== undefined || ay !== undefined){
22994             if(ax !== undefined && ay !== undefined){
22995                 el.setLeftTop(ax, ay);
22996             }else if(ax !== undefined){
22997                 el.setLeft(ax);
22998             }else if(ay !== undefined){
22999                 el.setTop(ay);
23000             }
23001             this.onPosition(ax, ay);
23002             this.fireEvent('move', this, ax, ay);
23003         }
23004         return this;
23005     },
23006
23007     /**
23008      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23009      * This method fires the move event.
23010      * @param {Number} x The new x position
23011      * @param {Number} y The new y position
23012      * @returns {Roo.BoxComponent} this
23013      */
23014     setPagePosition : function(x, y){
23015         this.pageX = x;
23016         this.pageY = y;
23017         if(!this.boxReady){
23018             return;
23019         }
23020         if(x === undefined || y === undefined){ // cannot translate undefined points
23021             return;
23022         }
23023         var p = this.el.translatePoints(x, y);
23024         this.setPosition(p.left, p.top);
23025         return this;
23026     },
23027
23028     // private
23029     onRender : function(ct, position){
23030         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23031         if(this.resizeEl){
23032             this.resizeEl = Roo.get(this.resizeEl);
23033         }
23034         if(this.positionEl){
23035             this.positionEl = Roo.get(this.positionEl);
23036         }
23037     },
23038
23039     // private
23040     afterRender : function(){
23041         Roo.BoxComponent.superclass.afterRender.call(this);
23042         this.boxReady = true;
23043         this.setSize(this.width, this.height);
23044         if(this.x || this.y){
23045             this.setPosition(this.x, this.y);
23046         }
23047         if(this.pageX || this.pageY){
23048             this.setPagePosition(this.pageX, this.pageY);
23049         }
23050     },
23051
23052     /**
23053      * Force the component's size to recalculate based on the underlying element's current height and width.
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     syncSize : function(){
23057         delete this.lastSize;
23058         this.setSize(this.el.getWidth(), this.el.getHeight());
23059         return this;
23060     },
23061
23062     /**
23063      * Called after the component is resized, this method is empty by default but can be implemented by any
23064      * subclass that needs to perform custom logic after a resize occurs.
23065      * @param {Number} adjWidth The box-adjusted width that was set
23066      * @param {Number} adjHeight The box-adjusted height that was set
23067      * @param {Number} rawWidth The width that was originally specified
23068      * @param {Number} rawHeight The height that was originally specified
23069      */
23070     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23071
23072     },
23073
23074     /**
23075      * Called after the component is moved, this method is empty by default but can be implemented by any
23076      * subclass that needs to perform custom logic after a move occurs.
23077      * @param {Number} x The new x position
23078      * @param {Number} y The new y position
23079      */
23080     onPosition : function(x, y){
23081
23082     },
23083
23084     // private
23085     adjustSize : function(w, h){
23086         if(this.autoWidth){
23087             w = 'auto';
23088         }
23089         if(this.autoHeight){
23090             h = 'auto';
23091         }
23092         return {width : w, height: h};
23093     },
23094
23095     // private
23096     adjustPosition : function(x, y){
23097         return {x : x, y: y};
23098     }
23099 });/*
23100  * Based on:
23101  * Ext JS Library 1.1.1
23102  * Copyright(c) 2006-2007, Ext JS, LLC.
23103  *
23104  * Originally Released Under LGPL - original licence link has changed is not relivant.
23105  *
23106  * Fork - LGPL
23107  * <script type="text/javascript">
23108  */
23109
23110
23111 /**
23112  * @class Roo.SplitBar
23113  * @extends Roo.util.Observable
23114  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23115  * <br><br>
23116  * Usage:
23117  * <pre><code>
23118 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23119                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23120 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23121 split.minSize = 100;
23122 split.maxSize = 600;
23123 split.animate = true;
23124 split.on('moved', splitterMoved);
23125 </code></pre>
23126  * @constructor
23127  * Create a new SplitBar
23128  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23129  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23130  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23131  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23132                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23133                         position of the SplitBar).
23134  */
23135 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23136     
23137     /** @private */
23138     this.el = Roo.get(dragElement, true);
23139     this.el.dom.unselectable = "on";
23140     /** @private */
23141     this.resizingEl = Roo.get(resizingElement, true);
23142
23143     /**
23144      * @private
23145      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23146      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23147      * @type Number
23148      */
23149     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23150     
23151     /**
23152      * The minimum size of the resizing element. (Defaults to 0)
23153      * @type Number
23154      */
23155     this.minSize = 0;
23156     
23157     /**
23158      * The maximum size of the resizing element. (Defaults to 2000)
23159      * @type Number
23160      */
23161     this.maxSize = 2000;
23162     
23163     /**
23164      * Whether to animate the transition to the new size
23165      * @type Boolean
23166      */
23167     this.animate = false;
23168     
23169     /**
23170      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23171      * @type Boolean
23172      */
23173     this.useShim = false;
23174     
23175     /** @private */
23176     this.shim = null;
23177     
23178     if(!existingProxy){
23179         /** @private */
23180         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23181     }else{
23182         this.proxy = Roo.get(existingProxy).dom;
23183     }
23184     /** @private */
23185     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23186     
23187     /** @private */
23188     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23189     
23190     /** @private */
23191     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23192     
23193     /** @private */
23194     this.dragSpecs = {};
23195     
23196     /**
23197      * @private The adapter to use to positon and resize elements
23198      */
23199     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23200     this.adapter.init(this);
23201     
23202     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23203         /** @private */
23204         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23205         this.el.addClass("x-splitbar-h");
23206     }else{
23207         /** @private */
23208         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23209         this.el.addClass("x-splitbar-v");
23210     }
23211     
23212     this.addEvents({
23213         /**
23214          * @event resize
23215          * Fires when the splitter is moved (alias for {@link #event-moved})
23216          * @param {Roo.SplitBar} this
23217          * @param {Number} newSize the new width or height
23218          */
23219         "resize" : true,
23220         /**
23221          * @event moved
23222          * Fires when the splitter is moved
23223          * @param {Roo.SplitBar} this
23224          * @param {Number} newSize the new width or height
23225          */
23226         "moved" : true,
23227         /**
23228          * @event beforeresize
23229          * Fires before the splitter is dragged
23230          * @param {Roo.SplitBar} this
23231          */
23232         "beforeresize" : true,
23233
23234         "beforeapply" : true
23235     });
23236
23237     Roo.util.Observable.call(this);
23238 };
23239
23240 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23241     onStartProxyDrag : function(x, y){
23242         this.fireEvent("beforeresize", this);
23243         if(!this.overlay){
23244             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23245             o.unselectable();
23246             o.enableDisplayMode("block");
23247             // all splitbars share the same overlay
23248             Roo.SplitBar.prototype.overlay = o;
23249         }
23250         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23251         this.overlay.show();
23252         Roo.get(this.proxy).setDisplayed("block");
23253         var size = this.adapter.getElementSize(this);
23254         this.activeMinSize = this.getMinimumSize();;
23255         this.activeMaxSize = this.getMaximumSize();;
23256         var c1 = size - this.activeMinSize;
23257         var c2 = Math.max(this.activeMaxSize - size, 0);
23258         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23259             this.dd.resetConstraints();
23260             this.dd.setXConstraint(
23261                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23262                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23263             );
23264             this.dd.setYConstraint(0, 0);
23265         }else{
23266             this.dd.resetConstraints();
23267             this.dd.setXConstraint(0, 0);
23268             this.dd.setYConstraint(
23269                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23270                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23271             );
23272          }
23273         this.dragSpecs.startSize = size;
23274         this.dragSpecs.startPoint = [x, y];
23275         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23276     },
23277     
23278     /** 
23279      * @private Called after the drag operation by the DDProxy
23280      */
23281     onEndProxyDrag : function(e){
23282         Roo.get(this.proxy).setDisplayed(false);
23283         var endPoint = Roo.lib.Event.getXY(e);
23284         if(this.overlay){
23285             this.overlay.hide();
23286         }
23287         var newSize;
23288         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23289             newSize = this.dragSpecs.startSize + 
23290                 (this.placement == Roo.SplitBar.LEFT ?
23291                     endPoint[0] - this.dragSpecs.startPoint[0] :
23292                     this.dragSpecs.startPoint[0] - endPoint[0]
23293                 );
23294         }else{
23295             newSize = this.dragSpecs.startSize + 
23296                 (this.placement == Roo.SplitBar.TOP ?
23297                     endPoint[1] - this.dragSpecs.startPoint[1] :
23298                     this.dragSpecs.startPoint[1] - endPoint[1]
23299                 );
23300         }
23301         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23302         if(newSize != this.dragSpecs.startSize){
23303             if(this.fireEvent('beforeapply', this, newSize) !== false){
23304                 this.adapter.setElementSize(this, newSize);
23305                 this.fireEvent("moved", this, newSize);
23306                 this.fireEvent("resize", this, newSize);
23307             }
23308         }
23309     },
23310     
23311     /**
23312      * Get the adapter this SplitBar uses
23313      * @return The adapter object
23314      */
23315     getAdapter : function(){
23316         return this.adapter;
23317     },
23318     
23319     /**
23320      * Set the adapter this SplitBar uses
23321      * @param {Object} adapter A SplitBar adapter object
23322      */
23323     setAdapter : function(adapter){
23324         this.adapter = adapter;
23325         this.adapter.init(this);
23326     },
23327     
23328     /**
23329      * Gets the minimum size for the resizing element
23330      * @return {Number} The minimum size
23331      */
23332     getMinimumSize : function(){
23333         return this.minSize;
23334     },
23335     
23336     /**
23337      * Sets the minimum size for the resizing element
23338      * @param {Number} minSize The minimum size
23339      */
23340     setMinimumSize : function(minSize){
23341         this.minSize = minSize;
23342     },
23343     
23344     /**
23345      * Gets the maximum size for the resizing element
23346      * @return {Number} The maximum size
23347      */
23348     getMaximumSize : function(){
23349         return this.maxSize;
23350     },
23351     
23352     /**
23353      * Sets the maximum size for the resizing element
23354      * @param {Number} maxSize The maximum size
23355      */
23356     setMaximumSize : function(maxSize){
23357         this.maxSize = maxSize;
23358     },
23359     
23360     /**
23361      * Sets the initialize size for the resizing element
23362      * @param {Number} size The initial size
23363      */
23364     setCurrentSize : function(size){
23365         var oldAnimate = this.animate;
23366         this.animate = false;
23367         this.adapter.setElementSize(this, size);
23368         this.animate = oldAnimate;
23369     },
23370     
23371     /**
23372      * Destroy this splitbar. 
23373      * @param {Boolean} removeEl True to remove the element
23374      */
23375     destroy : function(removeEl){
23376         if(this.shim){
23377             this.shim.remove();
23378         }
23379         this.dd.unreg();
23380         this.proxy.parentNode.removeChild(this.proxy);
23381         if(removeEl){
23382             this.el.remove();
23383         }
23384     }
23385 });
23386
23387 /**
23388  * @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.
23389  */
23390 Roo.SplitBar.createProxy = function(dir){
23391     var proxy = new Roo.Element(document.createElement("div"));
23392     proxy.unselectable();
23393     var cls = 'x-splitbar-proxy';
23394     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23395     document.body.appendChild(proxy.dom);
23396     return proxy.dom;
23397 };
23398
23399 /** 
23400  * @class Roo.SplitBar.BasicLayoutAdapter
23401  * Default Adapter. It assumes the splitter and resizing element are not positioned
23402  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23403  */
23404 Roo.SplitBar.BasicLayoutAdapter = function(){
23405 };
23406
23407 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23408     // do nothing for now
23409     init : function(s){
23410     
23411     },
23412     /**
23413      * Called before drag operations to get the current size of the resizing element. 
23414      * @param {Roo.SplitBar} s The SplitBar using this adapter
23415      */
23416      getElementSize : function(s){
23417         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23418             return s.resizingEl.getWidth();
23419         }else{
23420             return s.resizingEl.getHeight();
23421         }
23422     },
23423     
23424     /**
23425      * Called after drag operations to set the size of the resizing element.
23426      * @param {Roo.SplitBar} s The SplitBar using this adapter
23427      * @param {Number} newSize The new size to set
23428      * @param {Function} onComplete A function to be invoked when resizing is complete
23429      */
23430     setElementSize : function(s, newSize, onComplete){
23431         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23432             if(!s.animate){
23433                 s.resizingEl.setWidth(newSize);
23434                 if(onComplete){
23435                     onComplete(s, newSize);
23436                 }
23437             }else{
23438                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23439             }
23440         }else{
23441             
23442             if(!s.animate){
23443                 s.resizingEl.setHeight(newSize);
23444                 if(onComplete){
23445                     onComplete(s, newSize);
23446                 }
23447             }else{
23448                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23449             }
23450         }
23451     }
23452 };
23453
23454 /** 
23455  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23456  * @extends Roo.SplitBar.BasicLayoutAdapter
23457  * Adapter that  moves the splitter element to align with the resized sizing element. 
23458  * Used with an absolute positioned SplitBar.
23459  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23460  * document.body, make sure you assign an id to the body element.
23461  */
23462 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23463     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23464     this.container = Roo.get(container);
23465 };
23466
23467 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23468     init : function(s){
23469         this.basic.init(s);
23470     },
23471     
23472     getElementSize : function(s){
23473         return this.basic.getElementSize(s);
23474     },
23475     
23476     setElementSize : function(s, newSize, onComplete){
23477         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23478     },
23479     
23480     moveSplitter : function(s){
23481         var yes = Roo.SplitBar;
23482         switch(s.placement){
23483             case yes.LEFT:
23484                 s.el.setX(s.resizingEl.getRight());
23485                 break;
23486             case yes.RIGHT:
23487                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23488                 break;
23489             case yes.TOP:
23490                 s.el.setY(s.resizingEl.getBottom());
23491                 break;
23492             case yes.BOTTOM:
23493                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23494                 break;
23495         }
23496     }
23497 };
23498
23499 /**
23500  * Orientation constant - Create a vertical SplitBar
23501  * @static
23502  * @type Number
23503  */
23504 Roo.SplitBar.VERTICAL = 1;
23505
23506 /**
23507  * Orientation constant - Create a horizontal SplitBar
23508  * @static
23509  * @type Number
23510  */
23511 Roo.SplitBar.HORIZONTAL = 2;
23512
23513 /**
23514  * Placement constant - The resizing element is to the left of the splitter element
23515  * @static
23516  * @type Number
23517  */
23518 Roo.SplitBar.LEFT = 1;
23519
23520 /**
23521  * Placement constant - The resizing element is to the right of the splitter element
23522  * @static
23523  * @type Number
23524  */
23525 Roo.SplitBar.RIGHT = 2;
23526
23527 /**
23528  * Placement constant - The resizing element is positioned above the splitter element
23529  * @static
23530  * @type Number
23531  */
23532 Roo.SplitBar.TOP = 3;
23533
23534 /**
23535  * Placement constant - The resizing element is positioned under splitter element
23536  * @static
23537  * @type Number
23538  */
23539 Roo.SplitBar.BOTTOM = 4;
23540 /*
23541  * Based on:
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  *
23545  * Originally Released Under LGPL - original licence link has changed is not relivant.
23546  *
23547  * Fork - LGPL
23548  * <script type="text/javascript">
23549  */
23550
23551 /**
23552  * @class Roo.View
23553  * @extends Roo.util.Observable
23554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23555  * This class also supports single and multi selection modes. <br>
23556  * Create a data model bound view:
23557  <pre><code>
23558  var store = new Roo.data.Store(...);
23559
23560  var view = new Roo.View({
23561     el : "my-element",
23562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23563  
23564     singleSelect: true,
23565     selectedClass: "ydataview-selected",
23566     store: store
23567  });
23568
23569  // listen for node click?
23570  view.on("click", function(vw, index, node, e){
23571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23572  });
23573
23574  // load XML data
23575  dataModel.load("foobar.xml");
23576  </code></pre>
23577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23578  * <br><br>
23579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23581  * 
23582  * Note: old style constructor is still suported (container, template, config)
23583  * 
23584  * @constructor
23585  * Create a new View
23586  * @param {Object} config The config object
23587  * 
23588  */
23589 Roo.View = function(config, depreciated_tpl, depreciated_config){
23590     
23591     if (typeof(depreciated_tpl) == 'undefined') {
23592         // new way.. - universal constructor.
23593         Roo.apply(this, config);
23594         this.el  = Roo.get(this.el);
23595     } else {
23596         // old format..
23597         this.el  = Roo.get(config);
23598         this.tpl = depreciated_tpl;
23599         Roo.apply(this, depreciated_config);
23600     }
23601      
23602     
23603     if(typeof(this.tpl) == "string"){
23604         this.tpl = new Roo.Template(this.tpl);
23605     } else {
23606         // support xtype ctors..
23607         this.tpl = new Roo.factory(this.tpl, Roo);
23608     }
23609     
23610     
23611     this.tpl.compile();
23612    
23613
23614      
23615     /** @private */
23616     this.addEvents({
23617     /**
23618      * @event beforeclick
23619      * Fires before a click is processed. Returns false to cancel the default action.
23620      * @param {Roo.View} this
23621      * @param {Number} index The index of the target node
23622      * @param {HTMLElement} node The target node
23623      * @param {Roo.EventObject} e The raw event object
23624      */
23625         "beforeclick" : true,
23626     /**
23627      * @event click
23628      * Fires when a template node is clicked.
23629      * @param {Roo.View} this
23630      * @param {Number} index The index of the target node
23631      * @param {HTMLElement} node The target node
23632      * @param {Roo.EventObject} e The raw event object
23633      */
23634         "click" : true,
23635     /**
23636      * @event dblclick
23637      * Fires when a template node is double clicked.
23638      * @param {Roo.View} this
23639      * @param {Number} index The index of the target node
23640      * @param {HTMLElement} node The target node
23641      * @param {Roo.EventObject} e The raw event object
23642      */
23643         "dblclick" : true,
23644     /**
23645      * @event contextmenu
23646      * Fires when a template node is right clicked.
23647      * @param {Roo.View} this
23648      * @param {Number} index The index of the target node
23649      * @param {HTMLElement} node The target node
23650      * @param {Roo.EventObject} e The raw event object
23651      */
23652         "contextmenu" : true,
23653     /**
23654      * @event selectionchange
23655      * Fires when the selected nodes change.
23656      * @param {Roo.View} this
23657      * @param {Array} selections Array of the selected nodes
23658      */
23659         "selectionchange" : true,
23660
23661     /**
23662      * @event beforeselect
23663      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23664      * @param {Roo.View} this
23665      * @param {HTMLElement} node The node to be selected
23666      * @param {Array} selections Array of currently selected nodes
23667      */
23668         "beforeselect" : true
23669     });
23670
23671     this.el.on({
23672         "click": this.onClick,
23673         "dblclick": this.onDblClick,
23674         "contextmenu": this.onContextMenu,
23675         scope:this
23676     });
23677
23678     this.selections = [];
23679     this.nodes = [];
23680     this.cmp = new Roo.CompositeElementLite([]);
23681     if(this.store){
23682         this.store = Roo.factory(this.store, Roo.data);
23683         this.setStore(this.store, true);
23684     }
23685     Roo.View.superclass.constructor.call(this);
23686 };
23687
23688 Roo.extend(Roo.View, Roo.util.Observable, {
23689     
23690      /**
23691      * @cfg {Roo.data.Store} store Data store to load data from.
23692      */
23693     store : false,
23694     
23695     /**
23696      * @cfg {String|Roo.Element} el The container element.
23697      */
23698     el : '',
23699     
23700     /**
23701      * @cfg {String|Roo.Template} tpl The template used by this View 
23702      */
23703     tpl : false,
23704     
23705     /**
23706      * @cfg {String} selectedClass The css class to add to selected nodes
23707      */
23708     selectedClass : "x-view-selected",
23709      /**
23710      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23711      */
23712     emptyText : "",
23713     /**
23714      * @cfg {Boolean} multiSelect Allow multiple selection
23715      */
23716     
23717     multiSelect : false,
23718     /**
23719      * @cfg {Boolean} singleSelect Allow single selection
23720      */
23721     singleSelect:  false,
23722     
23723     /**
23724      * Returns the element this view is bound to.
23725      * @return {Roo.Element}
23726      */
23727     getEl : function(){
23728         return this.el;
23729     },
23730
23731     /**
23732      * Refreshes the view.
23733      */
23734     refresh : function(){
23735         var t = this.tpl;
23736         this.clearSelections();
23737         this.el.update("");
23738         var html = [];
23739         var records = this.store.getRange();
23740         if(records.length < 1){
23741             this.el.update(this.emptyText);
23742             return;
23743         }
23744         for(var i = 0, len = records.length; i < len; i++){
23745             var data = this.prepareData(records[i].data, i, records[i]);
23746             html[html.length] = t.apply(data);
23747         }
23748         this.el.update(html.join(""));
23749         this.nodes = this.el.dom.childNodes;
23750         this.updateIndexes(0);
23751     },
23752
23753     /**
23754      * Function to override to reformat the data that is sent to
23755      * the template for each node.
23756      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23757      * a JSON object for an UpdateManager bound view).
23758      */
23759     prepareData : function(data){
23760         return data;
23761     },
23762
23763     onUpdate : function(ds, record){
23764         this.clearSelections();
23765         var index = this.store.indexOf(record);
23766         var n = this.nodes[index];
23767         this.tpl.insertBefore(n, this.prepareData(record.data));
23768         n.parentNode.removeChild(n);
23769         this.updateIndexes(index, index);
23770     },
23771
23772     onAdd : function(ds, records, index){
23773         this.clearSelections();
23774         if(this.nodes.length == 0){
23775             this.refresh();
23776             return;
23777         }
23778         var n = this.nodes[index];
23779         for(var i = 0, len = records.length; i < len; i++){
23780             var d = this.prepareData(records[i].data);
23781             if(n){
23782                 this.tpl.insertBefore(n, d);
23783             }else{
23784                 this.tpl.append(this.el, d);
23785             }
23786         }
23787         this.updateIndexes(index);
23788     },
23789
23790     onRemove : function(ds, record, index){
23791         this.clearSelections();
23792         this.el.dom.removeChild(this.nodes[index]);
23793         this.updateIndexes(index);
23794     },
23795
23796     /**
23797      * Refresh an individual node.
23798      * @param {Number} index
23799      */
23800     refreshNode : function(index){
23801         this.onUpdate(this.store, this.store.getAt(index));
23802     },
23803
23804     updateIndexes : function(startIndex, endIndex){
23805         var ns = this.nodes;
23806         startIndex = startIndex || 0;
23807         endIndex = endIndex || ns.length - 1;
23808         for(var i = startIndex; i <= endIndex; i++){
23809             ns[i].nodeIndex = i;
23810         }
23811     },
23812
23813     /**
23814      * Changes the data store this view uses and refresh the view.
23815      * @param {Store} store
23816      */
23817     setStore : function(store, initial){
23818         if(!initial && this.store){
23819             this.store.un("datachanged", this.refresh);
23820             this.store.un("add", this.onAdd);
23821             this.store.un("remove", this.onRemove);
23822             this.store.un("update", this.onUpdate);
23823             this.store.un("clear", this.refresh);
23824         }
23825         if(store){
23826           
23827             store.on("datachanged", this.refresh, this);
23828             store.on("add", this.onAdd, this);
23829             store.on("remove", this.onRemove, this);
23830             store.on("update", this.onUpdate, this);
23831             store.on("clear", this.refresh, this);
23832         }
23833         
23834         if(store){
23835             this.refresh();
23836         }
23837     },
23838
23839     /**
23840      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23841      * @param {HTMLElement} node
23842      * @return {HTMLElement} The template node
23843      */
23844     findItemFromChild : function(node){
23845         var el = this.el.dom;
23846         if(!node || node.parentNode == el){
23847                     return node;
23848             }
23849             var p = node.parentNode;
23850             while(p && p != el){
23851             if(p.parentNode == el){
23852                 return p;
23853             }
23854             p = p.parentNode;
23855         }
23856             return null;
23857     },
23858
23859     /** @ignore */
23860     onClick : function(e){
23861         var item = this.findItemFromChild(e.getTarget());
23862         if(item){
23863             var index = this.indexOf(item);
23864             if(this.onItemClick(item, index, e) !== false){
23865                 this.fireEvent("click", this, index, item, e);
23866             }
23867         }else{
23868             this.clearSelections();
23869         }
23870     },
23871
23872     /** @ignore */
23873     onContextMenu : function(e){
23874         var item = this.findItemFromChild(e.getTarget());
23875         if(item){
23876             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23877         }
23878     },
23879
23880     /** @ignore */
23881     onDblClick : function(e){
23882         var item = this.findItemFromChild(e.getTarget());
23883         if(item){
23884             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23885         }
23886     },
23887
23888     onItemClick : function(item, index, e){
23889         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23890             return false;
23891         }
23892         if(this.multiSelect || this.singleSelect){
23893             if(this.multiSelect && e.shiftKey && this.lastSelection){
23894                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23895             }else{
23896                 this.select(item, this.multiSelect && e.ctrlKey);
23897                 this.lastSelection = item;
23898             }
23899             e.preventDefault();
23900         }
23901         return true;
23902     },
23903
23904     /**
23905      * Get the number of selected nodes.
23906      * @return {Number}
23907      */
23908     getSelectionCount : function(){
23909         return this.selections.length;
23910     },
23911
23912     /**
23913      * Get the currently selected nodes.
23914      * @return {Array} An array of HTMLElements
23915      */
23916     getSelectedNodes : function(){
23917         return this.selections;
23918     },
23919
23920     /**
23921      * Get the indexes of the selected nodes.
23922      * @return {Array}
23923      */
23924     getSelectedIndexes : function(){
23925         var indexes = [], s = this.selections;
23926         for(var i = 0, len = s.length; i < len; i++){
23927             indexes.push(s[i].nodeIndex);
23928         }
23929         return indexes;
23930     },
23931
23932     /**
23933      * Clear all selections
23934      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23935      */
23936     clearSelections : function(suppressEvent){
23937         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23938             this.cmp.elements = this.selections;
23939             this.cmp.removeClass(this.selectedClass);
23940             this.selections = [];
23941             if(!suppressEvent){
23942                 this.fireEvent("selectionchange", this, this.selections);
23943             }
23944         }
23945     },
23946
23947     /**
23948      * Returns true if the passed node is selected
23949      * @param {HTMLElement/Number} node The node or node index
23950      * @return {Boolean}
23951      */
23952     isSelected : function(node){
23953         var s = this.selections;
23954         if(s.length < 1){
23955             return false;
23956         }
23957         node = this.getNode(node);
23958         return s.indexOf(node) !== -1;
23959     },
23960
23961     /**
23962      * Selects nodes.
23963      * @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
23964      * @param {Boolean} keepExisting (optional) true to keep existing selections
23965      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23966      */
23967     select : function(nodeInfo, keepExisting, suppressEvent){
23968         if(nodeInfo instanceof Array){
23969             if(!keepExisting){
23970                 this.clearSelections(true);
23971             }
23972             for(var i = 0, len = nodeInfo.length; i < len; i++){
23973                 this.select(nodeInfo[i], true, true);
23974             }
23975         } else{
23976             var node = this.getNode(nodeInfo);
23977             if(node && !this.isSelected(node)){
23978                 if(!keepExisting){
23979                     this.clearSelections(true);
23980                 }
23981                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23982                     Roo.fly(node).addClass(this.selectedClass);
23983                     this.selections.push(node);
23984                     if(!suppressEvent){
23985                         this.fireEvent("selectionchange", this, this.selections);
23986                     }
23987                 }
23988             }
23989         }
23990     },
23991
23992     /**
23993      * Gets a template node.
23994      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23995      * @return {HTMLElement} The node or null if it wasn't found
23996      */
23997     getNode : function(nodeInfo){
23998         if(typeof nodeInfo == "string"){
23999             return document.getElementById(nodeInfo);
24000         }else if(typeof nodeInfo == "number"){
24001             return this.nodes[nodeInfo];
24002         }
24003         return nodeInfo;
24004     },
24005
24006     /**
24007      * Gets a range template nodes.
24008      * @param {Number} startIndex
24009      * @param {Number} endIndex
24010      * @return {Array} An array of nodes
24011      */
24012     getNodes : function(start, end){
24013         var ns = this.nodes;
24014         start = start || 0;
24015         end = typeof end == "undefined" ? ns.length - 1 : end;
24016         var nodes = [];
24017         if(start <= end){
24018             for(var i = start; i <= end; i++){
24019                 nodes.push(ns[i]);
24020             }
24021         } else{
24022             for(var i = start; i >= end; i--){
24023                 nodes.push(ns[i]);
24024             }
24025         }
24026         return nodes;
24027     },
24028
24029     /**
24030      * Finds the index of the passed node
24031      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24032      * @return {Number} The index of the node or -1
24033      */
24034     indexOf : function(node){
24035         node = this.getNode(node);
24036         if(typeof node.nodeIndex == "number"){
24037             return node.nodeIndex;
24038         }
24039         var ns = this.nodes;
24040         for(var i = 0, len = ns.length; i < len; i++){
24041             if(ns[i] == node){
24042                 return i;
24043             }
24044         }
24045         return -1;
24046     }
24047 });
24048 /*
24049  * Based on:
24050  * Ext JS Library 1.1.1
24051  * Copyright(c) 2006-2007, Ext JS, LLC.
24052  *
24053  * Originally Released Under LGPL - original licence link has changed is not relivant.
24054  *
24055  * Fork - LGPL
24056  * <script type="text/javascript">
24057  */
24058
24059 /**
24060  * @class Roo.JsonView
24061  * @extends Roo.View
24062  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24063 <pre><code>
24064 var view = new Roo.JsonView({
24065     container: "my-element",
24066     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24067     multiSelect: true, 
24068     jsonRoot: "data" 
24069 });
24070
24071 // listen for node click?
24072 view.on("click", function(vw, index, node, e){
24073     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24074 });
24075
24076 // direct load of JSON data
24077 view.load("foobar.php");
24078
24079 // Example from my blog list
24080 var tpl = new Roo.Template(
24081     '&lt;div class="entry"&gt;' +
24082     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24083     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24084     "&lt;/div&gt;&lt;hr /&gt;"
24085 );
24086
24087 var moreView = new Roo.JsonView({
24088     container :  "entry-list", 
24089     template : tpl,
24090     jsonRoot: "posts"
24091 });
24092 moreView.on("beforerender", this.sortEntries, this);
24093 moreView.load({
24094     url: "/blog/get-posts.php",
24095     params: "allposts=true",
24096     text: "Loading Blog Entries..."
24097 });
24098 </code></pre>
24099
24100 * Note: old code is supported with arguments : (container, template, config)
24101
24102
24103  * @constructor
24104  * Create a new JsonView
24105  * 
24106  * @param {Object} config The config object
24107  * 
24108  */
24109 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24110     
24111     
24112     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24113
24114     var um = this.el.getUpdateManager();
24115     um.setRenderer(this);
24116     um.on("update", this.onLoad, this);
24117     um.on("failure", this.onLoadException, this);
24118
24119     /**
24120      * @event beforerender
24121      * Fires before rendering of the downloaded JSON data.
24122      * @param {Roo.JsonView} this
24123      * @param {Object} data The JSON data loaded
24124      */
24125     /**
24126      * @event load
24127      * Fires when data is loaded.
24128      * @param {Roo.JsonView} this
24129      * @param {Object} data The JSON data loaded
24130      * @param {Object} response The raw Connect response object
24131      */
24132     /**
24133      * @event loadexception
24134      * Fires when loading fails.
24135      * @param {Roo.JsonView} this
24136      * @param {Object} response The raw Connect response object
24137      */
24138     this.addEvents({
24139         'beforerender' : true,
24140         'load' : true,
24141         'loadexception' : true
24142     });
24143 };
24144 Roo.extend(Roo.JsonView, Roo.View, {
24145     /**
24146      * @type {String} The root property in the loaded JSON object that contains the data
24147      */
24148     jsonRoot : "",
24149
24150     /**
24151      * Refreshes the view.
24152      */
24153     refresh : function(){
24154         this.clearSelections();
24155         this.el.update("");
24156         var html = [];
24157         var o = this.jsonData;
24158         if(o && o.length > 0){
24159             for(var i = 0, len = o.length; i < len; i++){
24160                 var data = this.prepareData(o[i], i, o);
24161                 html[html.length] = this.tpl.apply(data);
24162             }
24163         }else{
24164             html.push(this.emptyText);
24165         }
24166         this.el.update(html.join(""));
24167         this.nodes = this.el.dom.childNodes;
24168         this.updateIndexes(0);
24169     },
24170
24171     /**
24172      * 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.
24173      * @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:
24174      <pre><code>
24175      view.load({
24176          url: "your-url.php",
24177          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24178          callback: yourFunction,
24179          scope: yourObject, //(optional scope)
24180          discardUrl: false,
24181          nocache: false,
24182          text: "Loading...",
24183          timeout: 30,
24184          scripts: false
24185      });
24186      </code></pre>
24187      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24188      * 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.
24189      * @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}
24190      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24191      * @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.
24192      */
24193     load : function(){
24194         var um = this.el.getUpdateManager();
24195         um.update.apply(um, arguments);
24196     },
24197
24198     render : function(el, response){
24199         this.clearSelections();
24200         this.el.update("");
24201         var o;
24202         try{
24203             o = Roo.util.JSON.decode(response.responseText);
24204             if(this.jsonRoot){
24205                 
24206                 o = o[this.jsonRoot];
24207             }
24208         } catch(e){
24209         }
24210         /**
24211          * The current JSON data or null
24212          */
24213         this.jsonData = o;
24214         this.beforeRender();
24215         this.refresh();
24216     },
24217
24218 /**
24219  * Get the number of records in the current JSON dataset
24220  * @return {Number}
24221  */
24222     getCount : function(){
24223         return this.jsonData ? this.jsonData.length : 0;
24224     },
24225
24226 /**
24227  * Returns the JSON object for the specified node(s)
24228  * @param {HTMLElement/Array} node The node or an array of nodes
24229  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24230  * you get the JSON object for the node
24231  */
24232     getNodeData : function(node){
24233         if(node instanceof Array){
24234             var data = [];
24235             for(var i = 0, len = node.length; i < len; i++){
24236                 data.push(this.getNodeData(node[i]));
24237             }
24238             return data;
24239         }
24240         return this.jsonData[this.indexOf(node)] || null;
24241     },
24242
24243     beforeRender : function(){
24244         this.snapshot = this.jsonData;
24245         if(this.sortInfo){
24246             this.sort.apply(this, this.sortInfo);
24247         }
24248         this.fireEvent("beforerender", this, this.jsonData);
24249     },
24250
24251     onLoad : function(el, o){
24252         this.fireEvent("load", this, this.jsonData, o);
24253     },
24254
24255     onLoadException : function(el, o){
24256         this.fireEvent("loadexception", this, o);
24257     },
24258
24259 /**
24260  * Filter the data by a specific property.
24261  * @param {String} property A property on your JSON objects
24262  * @param {String/RegExp} value Either string that the property values
24263  * should start with, or a RegExp to test against the property
24264  */
24265     filter : function(property, value){
24266         if(this.jsonData){
24267             var data = [];
24268             var ss = this.snapshot;
24269             if(typeof value == "string"){
24270                 var vlen = value.length;
24271                 if(vlen == 0){
24272                     this.clearFilter();
24273                     return;
24274                 }
24275                 value = value.toLowerCase();
24276                 for(var i = 0, len = ss.length; i < len; i++){
24277                     var o = ss[i];
24278                     if(o[property].substr(0, vlen).toLowerCase() == value){
24279                         data.push(o);
24280                     }
24281                 }
24282             } else if(value.exec){ // regex?
24283                 for(var i = 0, len = ss.length; i < len; i++){
24284                     var o = ss[i];
24285                     if(value.test(o[property])){
24286                         data.push(o);
24287                     }
24288                 }
24289             } else{
24290                 return;
24291             }
24292             this.jsonData = data;
24293             this.refresh();
24294         }
24295     },
24296
24297 /**
24298  * Filter by a function. The passed function will be called with each
24299  * object in the current dataset. If the function returns true the value is kept,
24300  * otherwise it is filtered.
24301  * @param {Function} fn
24302  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24303  */
24304     filterBy : function(fn, scope){
24305         if(this.jsonData){
24306             var data = [];
24307             var ss = this.snapshot;
24308             for(var i = 0, len = ss.length; i < len; i++){
24309                 var o = ss[i];
24310                 if(fn.call(scope || this, o)){
24311                     data.push(o);
24312                 }
24313             }
24314             this.jsonData = data;
24315             this.refresh();
24316         }
24317     },
24318
24319 /**
24320  * Clears the current filter.
24321  */
24322     clearFilter : function(){
24323         if(this.snapshot && this.jsonData != this.snapshot){
24324             this.jsonData = this.snapshot;
24325             this.refresh();
24326         }
24327     },
24328
24329
24330 /**
24331  * Sorts the data for this view and refreshes it.
24332  * @param {String} property A property on your JSON objects to sort on
24333  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24334  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24335  */
24336     sort : function(property, dir, sortType){
24337         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24338         if(this.jsonData){
24339             var p = property;
24340             var dsc = dir && dir.toLowerCase() == "desc";
24341             var f = function(o1, o2){
24342                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24343                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24344                 ;
24345                 if(v1 < v2){
24346                     return dsc ? +1 : -1;
24347                 } else if(v1 > v2){
24348                     return dsc ? -1 : +1;
24349                 } else{
24350                     return 0;
24351                 }
24352             };
24353             this.jsonData.sort(f);
24354             this.refresh();
24355             if(this.jsonData != this.snapshot){
24356                 this.snapshot.sort(f);
24357             }
24358         }
24359     }
24360 });/*
24361  * Based on:
24362  * Ext JS Library 1.1.1
24363  * Copyright(c) 2006-2007, Ext JS, LLC.
24364  *
24365  * Originally Released Under LGPL - original licence link has changed is not relivant.
24366  *
24367  * Fork - LGPL
24368  * <script type="text/javascript">
24369  */
24370  
24371
24372 /**
24373  * @class Roo.ColorPalette
24374  * @extends Roo.Component
24375  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24376  * Here's an example of typical usage:
24377  * <pre><code>
24378 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24379 cp.render('my-div');
24380
24381 cp.on('select', function(palette, selColor){
24382     // do something with selColor
24383 });
24384 </code></pre>
24385  * @constructor
24386  * Create a new ColorPalette
24387  * @param {Object} config The config object
24388  */
24389 Roo.ColorPalette = function(config){
24390     Roo.ColorPalette.superclass.constructor.call(this, config);
24391     this.addEvents({
24392         /**
24393              * @event select
24394              * Fires when a color is selected
24395              * @param {ColorPalette} this
24396              * @param {String} color The 6-digit color hex code (without the # symbol)
24397              */
24398         select: true
24399     });
24400
24401     if(this.handler){
24402         this.on("select", this.handler, this.scope, true);
24403     }
24404 };
24405 Roo.extend(Roo.ColorPalette, Roo.Component, {
24406     /**
24407      * @cfg {String} itemCls
24408      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24409      */
24410     itemCls : "x-color-palette",
24411     /**
24412      * @cfg {String} value
24413      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24414      * the hex codes are case-sensitive.
24415      */
24416     value : null,
24417     clickEvent:'click',
24418     // private
24419     ctype: "Roo.ColorPalette",
24420
24421     /**
24422      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24423      */
24424     allowReselect : false,
24425
24426     /**
24427      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24428      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24429      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24430      * of colors with the width setting until the box is symmetrical.</p>
24431      * <p>You can override individual colors if needed:</p>
24432      * <pre><code>
24433 var cp = new Roo.ColorPalette();
24434 cp.colors[0] = "FF0000";  // change the first box to red
24435 </code></pre>
24436
24437 Or you can provide a custom array of your own for complete control:
24438 <pre><code>
24439 var cp = new Roo.ColorPalette();
24440 cp.colors = ["000000", "993300", "333300"];
24441 </code></pre>
24442      * @type Array
24443      */
24444     colors : [
24445         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24446         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24447         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24448         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24449         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24450     ],
24451
24452     // private
24453     onRender : function(container, position){
24454         var t = new Roo.MasterTemplate(
24455             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24456         );
24457         var c = this.colors;
24458         for(var i = 0, len = c.length; i < len; i++){
24459             t.add([c[i]]);
24460         }
24461         var el = document.createElement("div");
24462         el.className = this.itemCls;
24463         t.overwrite(el);
24464         container.dom.insertBefore(el, position);
24465         this.el = Roo.get(el);
24466         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24467         if(this.clickEvent != 'click'){
24468             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24469         }
24470     },
24471
24472     // private
24473     afterRender : function(){
24474         Roo.ColorPalette.superclass.afterRender.call(this);
24475         if(this.value){
24476             var s = this.value;
24477             this.value = null;
24478             this.select(s);
24479         }
24480     },
24481
24482     // private
24483     handleClick : function(e, t){
24484         e.preventDefault();
24485         if(!this.disabled){
24486             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24487             this.select(c.toUpperCase());
24488         }
24489     },
24490
24491     /**
24492      * Selects the specified color in the palette (fires the select event)
24493      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24494      */
24495     select : function(color){
24496         color = color.replace("#", "");
24497         if(color != this.value || this.allowReselect){
24498             var el = this.el;
24499             if(this.value){
24500                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24501             }
24502             el.child("a.color-"+color).addClass("x-color-palette-sel");
24503             this.value = color;
24504             this.fireEvent("select", this, color);
24505         }
24506     }
24507 });/*
24508  * Based on:
24509  * Ext JS Library 1.1.1
24510  * Copyright(c) 2006-2007, Ext JS, LLC.
24511  *
24512  * Originally Released Under LGPL - original licence link has changed is not relivant.
24513  *
24514  * Fork - LGPL
24515  * <script type="text/javascript">
24516  */
24517  
24518 /**
24519  * @class Roo.DatePicker
24520  * @extends Roo.Component
24521  * Simple date picker class.
24522  * @constructor
24523  * Create a new DatePicker
24524  * @param {Object} config The config object
24525  */
24526 Roo.DatePicker = function(config){
24527     Roo.DatePicker.superclass.constructor.call(this, config);
24528
24529     this.value = config && config.value ?
24530                  config.value.clearTime() : new Date().clearTime();
24531
24532     this.addEvents({
24533         /**
24534              * @event select
24535              * Fires when a date is selected
24536              * @param {DatePicker} this
24537              * @param {Date} date The selected date
24538              */
24539         select: true
24540     });
24541
24542     if(this.handler){
24543         this.on("select", this.handler,  this.scope || this);
24544     }
24545     // build the disabledDatesRE
24546     if(!this.disabledDatesRE && this.disabledDates){
24547         var dd = this.disabledDates;
24548         var re = "(?:";
24549         for(var i = 0; i < dd.length; i++){
24550             re += dd[i];
24551             if(i != dd.length-1) re += "|";
24552         }
24553         this.disabledDatesRE = new RegExp(re + ")");
24554     }
24555 };
24556
24557 Roo.extend(Roo.DatePicker, Roo.Component, {
24558     /**
24559      * @cfg {String} todayText
24560      * The text to display on the button that selects the current date (defaults to "Today")
24561      */
24562     todayText : "Today",
24563     /**
24564      * @cfg {String} okText
24565      * The text to display on the ok button
24566      */
24567     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24568     /**
24569      * @cfg {String} cancelText
24570      * The text to display on the cancel button
24571      */
24572     cancelText : "Cancel",
24573     /**
24574      * @cfg {String} todayTip
24575      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24576      */
24577     todayTip : "{0} (Spacebar)",
24578     /**
24579      * @cfg {Date} minDate
24580      * Minimum allowable date (JavaScript date object, defaults to null)
24581      */
24582     minDate : null,
24583     /**
24584      * @cfg {Date} maxDate
24585      * Maximum allowable date (JavaScript date object, defaults to null)
24586      */
24587     maxDate : null,
24588     /**
24589      * @cfg {String} minText
24590      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24591      */
24592     minText : "This date is before the minimum date",
24593     /**
24594      * @cfg {String} maxText
24595      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24596      */
24597     maxText : "This date is after the maximum date",
24598     /**
24599      * @cfg {String} format
24600      * The default date format string which can be overriden for localization support.  The format must be
24601      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24602      */
24603     format : "m/d/y",
24604     /**
24605      * @cfg {Array} disabledDays
24606      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24607      */
24608     disabledDays : null,
24609     /**
24610      * @cfg {String} disabledDaysText
24611      * The tooltip to display when the date falls on a disabled day (defaults to "")
24612      */
24613     disabledDaysText : "",
24614     /**
24615      * @cfg {RegExp} disabledDatesRE
24616      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24617      */
24618     disabledDatesRE : null,
24619     /**
24620      * @cfg {String} disabledDatesText
24621      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24622      */
24623     disabledDatesText : "",
24624     /**
24625      * @cfg {Boolean} constrainToViewport
24626      * True to constrain the date picker to the viewport (defaults to true)
24627      */
24628     constrainToViewport : true,
24629     /**
24630      * @cfg {Array} monthNames
24631      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24632      */
24633     monthNames : Date.monthNames,
24634     /**
24635      * @cfg {Array} dayNames
24636      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24637      */
24638     dayNames : Date.dayNames,
24639     /**
24640      * @cfg {String} nextText
24641      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24642      */
24643     nextText: 'Next Month (Control+Right)',
24644     /**
24645      * @cfg {String} prevText
24646      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24647      */
24648     prevText: 'Previous Month (Control+Left)',
24649     /**
24650      * @cfg {String} monthYearText
24651      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24652      */
24653     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24654     /**
24655      * @cfg {Number} startDay
24656      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24657      */
24658     startDay : 0,
24659     /**
24660      * @cfg {Bool} showClear
24661      * Show a clear button (usefull for date form elements that can be blank.)
24662      */
24663     
24664     showClear: false,
24665     
24666     /**
24667      * Sets the value of the date field
24668      * @param {Date} value The date to set
24669      */
24670     setValue : function(value){
24671         var old = this.value;
24672         this.value = value.clearTime(true);
24673         if(this.el){
24674             this.update(this.value);
24675         }
24676     },
24677
24678     /**
24679      * Gets the current selected value of the date field
24680      * @return {Date} The selected date
24681      */
24682     getValue : function(){
24683         return this.value;
24684     },
24685
24686     // private
24687     focus : function(){
24688         if(this.el){
24689             this.update(this.activeDate);
24690         }
24691     },
24692
24693     // private
24694     onRender : function(container, position){
24695         var m = [
24696              '<table cellspacing="0">',
24697                 '<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>',
24698                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24699         var dn = this.dayNames;
24700         for(var i = 0; i < 7; i++){
24701             var d = this.startDay+i;
24702             if(d > 6){
24703                 d = d-7;
24704             }
24705             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24706         }
24707         m[m.length] = "</tr></thead><tbody><tr>";
24708         for(var i = 0; i < 42; i++) {
24709             if(i % 7 == 0 && i != 0){
24710                 m[m.length] = "</tr><tr>";
24711             }
24712             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24713         }
24714         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24715             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24716
24717         var el = document.createElement("div");
24718         el.className = "x-date-picker";
24719         el.innerHTML = m.join("");
24720
24721         container.dom.insertBefore(el, position);
24722
24723         this.el = Roo.get(el);
24724         this.eventEl = Roo.get(el.firstChild);
24725
24726         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24727             handler: this.showPrevMonth,
24728             scope: this,
24729             preventDefault:true,
24730             stopDefault:true
24731         });
24732
24733         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24734             handler: this.showNextMonth,
24735             scope: this,
24736             preventDefault:true,
24737             stopDefault:true
24738         });
24739
24740         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24741
24742         this.monthPicker = this.el.down('div.x-date-mp');
24743         this.monthPicker.enableDisplayMode('block');
24744         
24745         var kn = new Roo.KeyNav(this.eventEl, {
24746             "left" : function(e){
24747                 e.ctrlKey ?
24748                     this.showPrevMonth() :
24749                     this.update(this.activeDate.add("d", -1));
24750             },
24751
24752             "right" : function(e){
24753                 e.ctrlKey ?
24754                     this.showNextMonth() :
24755                     this.update(this.activeDate.add("d", 1));
24756             },
24757
24758             "up" : function(e){
24759                 e.ctrlKey ?
24760                     this.showNextYear() :
24761                     this.update(this.activeDate.add("d", -7));
24762             },
24763
24764             "down" : function(e){
24765                 e.ctrlKey ?
24766                     this.showPrevYear() :
24767                     this.update(this.activeDate.add("d", 7));
24768             },
24769
24770             "pageUp" : function(e){
24771                 this.showNextMonth();
24772             },
24773
24774             "pageDown" : function(e){
24775                 this.showPrevMonth();
24776             },
24777
24778             "enter" : function(e){
24779                 e.stopPropagation();
24780                 return true;
24781             },
24782
24783             scope : this
24784         });
24785
24786         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24787
24788         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24789
24790         this.el.unselectable();
24791         
24792         this.cells = this.el.select("table.x-date-inner tbody td");
24793         this.textNodes = this.el.query("table.x-date-inner tbody span");
24794
24795         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24796             text: "&#160;",
24797             tooltip: this.monthYearText
24798         });
24799
24800         this.mbtn.on('click', this.showMonthPicker, this);
24801         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24802
24803
24804         var today = (new Date()).dateFormat(this.format);
24805         
24806         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24807         if (this.showClear) {
24808             baseTb.add( new Roo.Toolbar.Fill());
24809         }
24810         baseTb.add({
24811             text: String.format(this.todayText, today),
24812             tooltip: String.format(this.todayTip, today),
24813             handler: this.selectToday,
24814             scope: this
24815         });
24816         
24817         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24818             
24819         //});
24820         if (this.showClear) {
24821             
24822             baseTb.add( new Roo.Toolbar.Fill());
24823             baseTb.add({
24824                 text: '&#160;',
24825                 cls: 'x-btn-icon x-btn-clear',
24826                 handler: function() {
24827                     //this.value = '';
24828                     this.fireEvent("select", this, '');
24829                 },
24830                 scope: this
24831             });
24832         }
24833         
24834         
24835         if(Roo.isIE){
24836             this.el.repaint();
24837         }
24838         this.update(this.value);
24839     },
24840
24841     createMonthPicker : function(){
24842         if(!this.monthPicker.dom.firstChild){
24843             var buf = ['<table border="0" cellspacing="0">'];
24844             for(var i = 0; i < 6; i++){
24845                 buf.push(
24846                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24847                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24848                     i == 0 ?
24849                     '<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>' :
24850                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24851                 );
24852             }
24853             buf.push(
24854                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24855                     this.okText,
24856                     '</button><button type="button" class="x-date-mp-cancel">',
24857                     this.cancelText,
24858                     '</button></td></tr>',
24859                 '</table>'
24860             );
24861             this.monthPicker.update(buf.join(''));
24862             this.monthPicker.on('click', this.onMonthClick, this);
24863             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24864
24865             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24866             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24867
24868             this.mpMonths.each(function(m, a, i){
24869                 i += 1;
24870                 if((i%2) == 0){
24871                     m.dom.xmonth = 5 + Math.round(i * .5);
24872                 }else{
24873                     m.dom.xmonth = Math.round((i-1) * .5);
24874                 }
24875             });
24876         }
24877     },
24878
24879     showMonthPicker : function(){
24880         this.createMonthPicker();
24881         var size = this.el.getSize();
24882         this.monthPicker.setSize(size);
24883         this.monthPicker.child('table').setSize(size);
24884
24885         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24886         this.updateMPMonth(this.mpSelMonth);
24887         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24888         this.updateMPYear(this.mpSelYear);
24889
24890         this.monthPicker.slideIn('t', {duration:.2});
24891     },
24892
24893     updateMPYear : function(y){
24894         this.mpyear = y;
24895         var ys = this.mpYears.elements;
24896         for(var i = 1; i <= 10; i++){
24897             var td = ys[i-1], y2;
24898             if((i%2) == 0){
24899                 y2 = y + Math.round(i * .5);
24900                 td.firstChild.innerHTML = y2;
24901                 td.xyear = y2;
24902             }else{
24903                 y2 = y - (5-Math.round(i * .5));
24904                 td.firstChild.innerHTML = y2;
24905                 td.xyear = y2;
24906             }
24907             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24908         }
24909     },
24910
24911     updateMPMonth : function(sm){
24912         this.mpMonths.each(function(m, a, i){
24913             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24914         });
24915     },
24916
24917     selectMPMonth: function(m){
24918         
24919     },
24920
24921     onMonthClick : function(e, t){
24922         e.stopEvent();
24923         var el = new Roo.Element(t), pn;
24924         if(el.is('button.x-date-mp-cancel')){
24925             this.hideMonthPicker();
24926         }
24927         else if(el.is('button.x-date-mp-ok')){
24928             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24929             this.hideMonthPicker();
24930         }
24931         else if(pn = el.up('td.x-date-mp-month', 2)){
24932             this.mpMonths.removeClass('x-date-mp-sel');
24933             pn.addClass('x-date-mp-sel');
24934             this.mpSelMonth = pn.dom.xmonth;
24935         }
24936         else if(pn = el.up('td.x-date-mp-year', 2)){
24937             this.mpYears.removeClass('x-date-mp-sel');
24938             pn.addClass('x-date-mp-sel');
24939             this.mpSelYear = pn.dom.xyear;
24940         }
24941         else if(el.is('a.x-date-mp-prev')){
24942             this.updateMPYear(this.mpyear-10);
24943         }
24944         else if(el.is('a.x-date-mp-next')){
24945             this.updateMPYear(this.mpyear+10);
24946         }
24947     },
24948
24949     onMonthDblClick : function(e, t){
24950         e.stopEvent();
24951         var el = new Roo.Element(t), pn;
24952         if(pn = el.up('td.x-date-mp-month', 2)){
24953             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24954             this.hideMonthPicker();
24955         }
24956         else if(pn = el.up('td.x-date-mp-year', 2)){
24957             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24958             this.hideMonthPicker();
24959         }
24960     },
24961
24962     hideMonthPicker : function(disableAnim){
24963         if(this.monthPicker){
24964             if(disableAnim === true){
24965                 this.monthPicker.hide();
24966             }else{
24967                 this.monthPicker.slideOut('t', {duration:.2});
24968             }
24969         }
24970     },
24971
24972     // private
24973     showPrevMonth : function(e){
24974         this.update(this.activeDate.add("mo", -1));
24975     },
24976
24977     // private
24978     showNextMonth : function(e){
24979         this.update(this.activeDate.add("mo", 1));
24980     },
24981
24982     // private
24983     showPrevYear : function(){
24984         this.update(this.activeDate.add("y", -1));
24985     },
24986
24987     // private
24988     showNextYear : function(){
24989         this.update(this.activeDate.add("y", 1));
24990     },
24991
24992     // private
24993     handleMouseWheel : function(e){
24994         var delta = e.getWheelDelta();
24995         if(delta > 0){
24996             this.showPrevMonth();
24997             e.stopEvent();
24998         } else if(delta < 0){
24999             this.showNextMonth();
25000             e.stopEvent();
25001         }
25002     },
25003
25004     // private
25005     handleDateClick : function(e, t){
25006         e.stopEvent();
25007         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25008             this.setValue(new Date(t.dateValue));
25009             this.fireEvent("select", this, this.value);
25010         }
25011     },
25012
25013     // private
25014     selectToday : function(){
25015         this.setValue(new Date().clearTime());
25016         this.fireEvent("select", this, this.value);
25017     },
25018
25019     // private
25020     update : function(date){
25021         var vd = this.activeDate;
25022         this.activeDate = date;
25023         if(vd && this.el){
25024             var t = date.getTime();
25025             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25026                 this.cells.removeClass("x-date-selected");
25027                 this.cells.each(function(c){
25028                    if(c.dom.firstChild.dateValue == t){
25029                        c.addClass("x-date-selected");
25030                        setTimeout(function(){
25031                             try{c.dom.firstChild.focus();}catch(e){}
25032                        }, 50);
25033                        return false;
25034                    }
25035                 });
25036                 return;
25037             }
25038         }
25039         var days = date.getDaysInMonth();
25040         var firstOfMonth = date.getFirstDateOfMonth();
25041         var startingPos = firstOfMonth.getDay()-this.startDay;
25042
25043         if(startingPos <= this.startDay){
25044             startingPos += 7;
25045         }
25046
25047         var pm = date.add("mo", -1);
25048         var prevStart = pm.getDaysInMonth()-startingPos;
25049
25050         var cells = this.cells.elements;
25051         var textEls = this.textNodes;
25052         days += startingPos;
25053
25054         // convert everything to numbers so it's fast
25055         var day = 86400000;
25056         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25057         var today = new Date().clearTime().getTime();
25058         var sel = date.clearTime().getTime();
25059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25061         var ddMatch = this.disabledDatesRE;
25062         var ddText = this.disabledDatesText;
25063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25064         var ddaysText = this.disabledDaysText;
25065         var format = this.format;
25066
25067         var setCellClass = function(cal, cell){
25068             cell.title = "";
25069             var t = d.getTime();
25070             cell.firstChild.dateValue = t;
25071             if(t == today){
25072                 cell.className += " x-date-today";
25073                 cell.title = cal.todayText;
25074             }
25075             if(t == sel){
25076                 cell.className += " x-date-selected";
25077                 setTimeout(function(){
25078                     try{cell.firstChild.focus();}catch(e){}
25079                 }, 50);
25080             }
25081             // disabling
25082             if(t < min) {
25083                 cell.className = " x-date-disabled";
25084                 cell.title = cal.minText;
25085                 return;
25086             }
25087             if(t > max) {
25088                 cell.className = " x-date-disabled";
25089                 cell.title = cal.maxText;
25090                 return;
25091             }
25092             if(ddays){
25093                 if(ddays.indexOf(d.getDay()) != -1){
25094                     cell.title = ddaysText;
25095                     cell.className = " x-date-disabled";
25096                 }
25097             }
25098             if(ddMatch && format){
25099                 var fvalue = d.dateFormat(format);
25100                 if(ddMatch.test(fvalue)){
25101                     cell.title = ddText.replace("%0", fvalue);
25102                     cell.className = " x-date-disabled";
25103                 }
25104             }
25105         };
25106
25107         var i = 0;
25108         for(; i < startingPos; i++) {
25109             textEls[i].innerHTML = (++prevStart);
25110             d.setDate(d.getDate()+1);
25111             cells[i].className = "x-date-prevday";
25112             setCellClass(this, cells[i]);
25113         }
25114         for(; i < days; i++){
25115             intDay = i - startingPos + 1;
25116             textEls[i].innerHTML = (intDay);
25117             d.setDate(d.getDate()+1);
25118             cells[i].className = "x-date-active";
25119             setCellClass(this, cells[i]);
25120         }
25121         var extraDays = 0;
25122         for(; i < 42; i++) {
25123              textEls[i].innerHTML = (++extraDays);
25124              d.setDate(d.getDate()+1);
25125              cells[i].className = "x-date-nextday";
25126              setCellClass(this, cells[i]);
25127         }
25128
25129         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25130
25131         if(!this.internalRender){
25132             var main = this.el.dom.firstChild;
25133             var w = main.offsetWidth;
25134             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25135             Roo.fly(main).setWidth(w);
25136             this.internalRender = true;
25137             // opera does not respect the auto grow header center column
25138             // then, after it gets a width opera refuses to recalculate
25139             // without a second pass
25140             if(Roo.isOpera && !this.secondPass){
25141                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25142                 this.secondPass = true;
25143                 this.update.defer(10, this, [date]);
25144             }
25145         }
25146     }
25147 });/*
25148  * Based on:
25149  * Ext JS Library 1.1.1
25150  * Copyright(c) 2006-2007, Ext JS, LLC.
25151  *
25152  * Originally Released Under LGPL - original licence link has changed is not relivant.
25153  *
25154  * Fork - LGPL
25155  * <script type="text/javascript">
25156  */
25157 /**
25158  * @class Roo.TabPanel
25159  * @extends Roo.util.Observable
25160  * A lightweight tab container.
25161  * <br><br>
25162  * Usage:
25163  * <pre><code>
25164 // basic tabs 1, built from existing content
25165 var tabs = new Roo.TabPanel("tabs1");
25166 tabs.addTab("script", "View Script");
25167 tabs.addTab("markup", "View Markup");
25168 tabs.activate("script");
25169
25170 // more advanced tabs, built from javascript
25171 var jtabs = new Roo.TabPanel("jtabs");
25172 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25173
25174 // set up the UpdateManager
25175 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25176 var updater = tab2.getUpdateManager();
25177 updater.setDefaultUrl("ajax1.htm");
25178 tab2.on('activate', updater.refresh, updater, true);
25179
25180 // Use setUrl for Ajax loading
25181 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25182 tab3.setUrl("ajax2.htm", null, true);
25183
25184 // Disabled tab
25185 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25186 tab4.disable();
25187
25188 jtabs.activate("jtabs-1");
25189  * </code></pre>
25190  * @constructor
25191  * Create a new TabPanel.
25192  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25193  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25194  */
25195 Roo.TabPanel = function(container, config){
25196     /**
25197     * The container element for this TabPanel.
25198     * @type Roo.Element
25199     */
25200     this.el = Roo.get(container, true);
25201     if(config){
25202         if(typeof config == "boolean"){
25203             this.tabPosition = config ? "bottom" : "top";
25204         }else{
25205             Roo.apply(this, config);
25206         }
25207     }
25208     if(this.tabPosition == "bottom"){
25209         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25210         this.el.addClass("x-tabs-bottom");
25211     }
25212     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25213     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25214     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25215     if(Roo.isIE){
25216         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25217     }
25218     if(this.tabPosition != "bottom"){
25219     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25220      * @type Roo.Element
25221      */
25222       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25223       this.el.addClass("x-tabs-top");
25224     }
25225     this.items = [];
25226
25227     this.bodyEl.setStyle("position", "relative");
25228
25229     this.active = null;
25230     this.activateDelegate = this.activate.createDelegate(this);
25231
25232     this.addEvents({
25233         /**
25234          * @event tabchange
25235          * Fires when the active tab changes
25236          * @param {Roo.TabPanel} this
25237          * @param {Roo.TabPanelItem} activePanel The new active tab
25238          */
25239         "tabchange": true,
25240         /**
25241          * @event beforetabchange
25242          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25243          * @param {Roo.TabPanel} this
25244          * @param {Object} e Set cancel to true on this object to cancel the tab change
25245          * @param {Roo.TabPanelItem} tab The tab being changed to
25246          */
25247         "beforetabchange" : true
25248     });
25249
25250     Roo.EventManager.onWindowResize(this.onResize, this);
25251     this.cpad = this.el.getPadding("lr");
25252     this.hiddenCount = 0;
25253
25254     Roo.TabPanel.superclass.constructor.call(this);
25255 };
25256
25257 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25258         /*
25259          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25260          */
25261     tabPosition : "top",
25262         /*
25263          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25264          */
25265     currentTabWidth : 0,
25266         /*
25267          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25268          */
25269     minTabWidth : 40,
25270         /*
25271          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25272          */
25273     maxTabWidth : 250,
25274         /*
25275          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25276          */
25277     preferredTabWidth : 175,
25278         /*
25279          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25280          */
25281     resizeTabs : false,
25282         /*
25283          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25284          */
25285     monitorResize : true,
25286
25287     /**
25288      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25289      * @param {String} id The id of the div to use <b>or create</b>
25290      * @param {String} text The text for the tab
25291      * @param {String} content (optional) Content to put in the TabPanelItem body
25292      * @param {Boolean} closable (optional) True to create a close icon on the tab
25293      * @return {Roo.TabPanelItem} The created TabPanelItem
25294      */
25295     addTab : function(id, text, content, closable){
25296         var item = new Roo.TabPanelItem(this, id, text, closable);
25297         this.addTabItem(item);
25298         if(content){
25299             item.setContent(content);
25300         }
25301         return item;
25302     },
25303
25304     /**
25305      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25306      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25307      * @return {Roo.TabPanelItem}
25308      */
25309     getTab : function(id){
25310         return this.items[id];
25311     },
25312
25313     /**
25314      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25315      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25316      */
25317     hideTab : function(id){
25318         var t = this.items[id];
25319         if(!t.isHidden()){
25320            t.setHidden(true);
25321            this.hiddenCount++;
25322            this.autoSizeTabs();
25323         }
25324     },
25325
25326     /**
25327      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25328      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25329      */
25330     unhideTab : function(id){
25331         var t = this.items[id];
25332         if(t.isHidden()){
25333            t.setHidden(false);
25334            this.hiddenCount--;
25335            this.autoSizeTabs();
25336         }
25337     },
25338
25339     /**
25340      * Adds an existing {@link Roo.TabPanelItem}.
25341      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25342      */
25343     addTabItem : function(item){
25344         this.items[item.id] = item;
25345         this.items.push(item);
25346         if(this.resizeTabs){
25347            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25348            this.autoSizeTabs();
25349         }else{
25350             item.autoSize();
25351         }
25352     },
25353
25354     /**
25355      * Removes a {@link Roo.TabPanelItem}.
25356      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25357      */
25358     removeTab : function(id){
25359         var items = this.items;
25360         var tab = items[id];
25361         if(!tab) { return; }
25362         var index = items.indexOf(tab);
25363         if(this.active == tab && items.length > 1){
25364             var newTab = this.getNextAvailable(index);
25365             if(newTab) {
25366                 newTab.activate();
25367             }
25368         }
25369         this.stripEl.dom.removeChild(tab.pnode.dom);
25370         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25371             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25372         }
25373         items.splice(index, 1);
25374         delete this.items[tab.id];
25375         tab.fireEvent("close", tab);
25376         tab.purgeListeners();
25377         this.autoSizeTabs();
25378     },
25379
25380     getNextAvailable : function(start){
25381         var items = this.items;
25382         var index = start;
25383         // look for a next tab that will slide over to
25384         // replace the one being removed
25385         while(index < items.length){
25386             var item = items[++index];
25387             if(item && !item.isHidden()){
25388                 return item;
25389             }
25390         }
25391         // if one isn't found select the previous tab (on the left)
25392         index = start;
25393         while(index >= 0){
25394             var item = items[--index];
25395             if(item && !item.isHidden()){
25396                 return item;
25397             }
25398         }
25399         return null;
25400     },
25401
25402     /**
25403      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25404      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25405      */
25406     disableTab : function(id){
25407         var tab = this.items[id];
25408         if(tab && this.active != tab){
25409             tab.disable();
25410         }
25411     },
25412
25413     /**
25414      * Enables a {@link Roo.TabPanelItem} that is disabled.
25415      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25416      */
25417     enableTab : function(id){
25418         var tab = this.items[id];
25419         tab.enable();
25420     },
25421
25422     /**
25423      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25424      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25425      * @return {Roo.TabPanelItem} The TabPanelItem.
25426      */
25427     activate : function(id){
25428         var tab = this.items[id];
25429         if(!tab){
25430             return null;
25431         }
25432         if(tab == this.active || tab.disabled){
25433             return tab;
25434         }
25435         var e = {};
25436         this.fireEvent("beforetabchange", this, e, tab);
25437         if(e.cancel !== true && !tab.disabled){
25438             if(this.active){
25439                 this.active.hide();
25440             }
25441             this.active = this.items[id];
25442             this.active.show();
25443             this.fireEvent("tabchange", this, this.active);
25444         }
25445         return tab;
25446     },
25447
25448     /**
25449      * Gets the active {@link Roo.TabPanelItem}.
25450      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25451      */
25452     getActiveTab : function(){
25453         return this.active;
25454     },
25455
25456     /**
25457      * Updates the tab body element to fit the height of the container element
25458      * for overflow scrolling
25459      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25460      */
25461     syncHeight : function(targetHeight){
25462         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25463         var bm = this.bodyEl.getMargins();
25464         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25465         this.bodyEl.setHeight(newHeight);
25466         return newHeight;
25467     },
25468
25469     onResize : function(){
25470         if(this.monitorResize){
25471             this.autoSizeTabs();
25472         }
25473     },
25474
25475     /**
25476      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25477      */
25478     beginUpdate : function(){
25479         this.updating = true;
25480     },
25481
25482     /**
25483      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25484      */
25485     endUpdate : function(){
25486         this.updating = false;
25487         this.autoSizeTabs();
25488     },
25489
25490     /**
25491      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25492      */
25493     autoSizeTabs : function(){
25494         var count = this.items.length;
25495         var vcount = count - this.hiddenCount;
25496         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25497         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25498         var availWidth = Math.floor(w / vcount);
25499         var b = this.stripBody;
25500         if(b.getWidth() > w){
25501             var tabs = this.items;
25502             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25503             if(availWidth < this.minTabWidth){
25504                 /*if(!this.sleft){    // incomplete scrolling code
25505                     this.createScrollButtons();
25506                 }
25507                 this.showScroll();
25508                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25509             }
25510         }else{
25511             if(this.currentTabWidth < this.preferredTabWidth){
25512                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25513             }
25514         }
25515     },
25516
25517     /**
25518      * Returns the number of tabs in this TabPanel.
25519      * @return {Number}
25520      */
25521      getCount : function(){
25522          return this.items.length;
25523      },
25524
25525     /**
25526      * Resizes all the tabs to the passed width
25527      * @param {Number} The new width
25528      */
25529     setTabWidth : function(width){
25530         this.currentTabWidth = width;
25531         for(var i = 0, len = this.items.length; i < len; i++) {
25532                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25533         }
25534     },
25535
25536     /**
25537      * Destroys this TabPanel
25538      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25539      */
25540     destroy : function(removeEl){
25541         Roo.EventManager.removeResizeListener(this.onResize, this);
25542         for(var i = 0, len = this.items.length; i < len; i++){
25543             this.items[i].purgeListeners();
25544         }
25545         if(removeEl === true){
25546             this.el.update("");
25547             this.el.remove();
25548         }
25549     }
25550 });
25551
25552 /**
25553  * @class Roo.TabPanelItem
25554  * @extends Roo.util.Observable
25555  * Represents an individual item (tab plus body) in a TabPanel.
25556  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25557  * @param {String} id The id of this TabPanelItem
25558  * @param {String} text The text for the tab of this TabPanelItem
25559  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25560  */
25561 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25562     /**
25563      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25564      * @type Roo.TabPanel
25565      */
25566     this.tabPanel = tabPanel;
25567     /**
25568      * The id for this TabPanelItem
25569      * @type String
25570      */
25571     this.id = id;
25572     /** @private */
25573     this.disabled = false;
25574     /** @private */
25575     this.text = text;
25576     /** @private */
25577     this.loaded = false;
25578     this.closable = closable;
25579
25580     /**
25581      * The body element for this TabPanelItem.
25582      * @type Roo.Element
25583      */
25584     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25585     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25586     this.bodyEl.setStyle("display", "block");
25587     this.bodyEl.setStyle("zoom", "1");
25588     this.hideAction();
25589
25590     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25591     /** @private */
25592     this.el = Roo.get(els.el, true);
25593     this.inner = Roo.get(els.inner, true);
25594     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25595     this.pnode = Roo.get(els.el.parentNode, true);
25596     this.el.on("mousedown", this.onTabMouseDown, this);
25597     this.el.on("click", this.onTabClick, this);
25598     /** @private */
25599     if(closable){
25600         var c = Roo.get(els.close, true);
25601         c.dom.title = this.closeText;
25602         c.addClassOnOver("close-over");
25603         c.on("click", this.closeClick, this);
25604      }
25605
25606     this.addEvents({
25607          /**
25608          * @event activate
25609          * Fires when this tab becomes the active tab.
25610          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25611          * @param {Roo.TabPanelItem} this
25612          */
25613         "activate": true,
25614         /**
25615          * @event beforeclose
25616          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25617          * @param {Roo.TabPanelItem} this
25618          * @param {Object} e Set cancel to true on this object to cancel the close.
25619          */
25620         "beforeclose": true,
25621         /**
25622          * @event close
25623          * Fires when this tab is closed.
25624          * @param {Roo.TabPanelItem} this
25625          */
25626          "close": true,
25627         /**
25628          * @event deactivate
25629          * Fires when this tab is no longer the active tab.
25630          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25631          * @param {Roo.TabPanelItem} this
25632          */
25633          "deactivate" : true
25634     });
25635     this.hidden = false;
25636
25637     Roo.TabPanelItem.superclass.constructor.call(this);
25638 };
25639
25640 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25641     purgeListeners : function(){
25642        Roo.util.Observable.prototype.purgeListeners.call(this);
25643        this.el.removeAllListeners();
25644     },
25645     /**
25646      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25647      */
25648     show : function(){
25649         this.pnode.addClass("on");
25650         this.showAction();
25651         if(Roo.isOpera){
25652             this.tabPanel.stripWrap.repaint();
25653         }
25654         this.fireEvent("activate", this.tabPanel, this);
25655     },
25656
25657     /**
25658      * Returns true if this tab is the active tab.
25659      * @return {Boolean}
25660      */
25661     isActive : function(){
25662         return this.tabPanel.getActiveTab() == this;
25663     },
25664
25665     /**
25666      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25667      */
25668     hide : function(){
25669         this.pnode.removeClass("on");
25670         this.hideAction();
25671         this.fireEvent("deactivate", this.tabPanel, this);
25672     },
25673
25674     hideAction : function(){
25675         this.bodyEl.hide();
25676         this.bodyEl.setStyle("position", "absolute");
25677         this.bodyEl.setLeft("-20000px");
25678         this.bodyEl.setTop("-20000px");
25679     },
25680
25681     showAction : function(){
25682         this.bodyEl.setStyle("position", "relative");
25683         this.bodyEl.setTop("");
25684         this.bodyEl.setLeft("");
25685         this.bodyEl.show();
25686     },
25687
25688     /**
25689      * Set the tooltip for the tab.
25690      * @param {String} tooltip The tab's tooltip
25691      */
25692     setTooltip : function(text){
25693         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25694             this.textEl.dom.qtip = text;
25695             this.textEl.dom.removeAttribute('title');
25696         }else{
25697             this.textEl.dom.title = text;
25698         }
25699     },
25700
25701     onTabClick : function(e){
25702         e.preventDefault();
25703         this.tabPanel.activate(this.id);
25704     },
25705
25706     onTabMouseDown : function(e){
25707         e.preventDefault();
25708         this.tabPanel.activate(this.id);
25709     },
25710
25711     getWidth : function(){
25712         return this.inner.getWidth();
25713     },
25714
25715     setWidth : function(width){
25716         var iwidth = width - this.pnode.getPadding("lr");
25717         this.inner.setWidth(iwidth);
25718         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25719         this.pnode.setWidth(width);
25720     },
25721
25722     /**
25723      * Show or hide the tab
25724      * @param {Boolean} hidden True to hide or false to show.
25725      */
25726     setHidden : function(hidden){
25727         this.hidden = hidden;
25728         this.pnode.setStyle("display", hidden ? "none" : "");
25729     },
25730
25731     /**
25732      * Returns true if this tab is "hidden"
25733      * @return {Boolean}
25734      */
25735     isHidden : function(){
25736         return this.hidden;
25737     },
25738
25739     /**
25740      * Returns the text for this tab
25741      * @return {String}
25742      */
25743     getText : function(){
25744         return this.text;
25745     },
25746
25747     autoSize : function(){
25748         //this.el.beginMeasure();
25749         this.textEl.setWidth(1);
25750         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25751         //this.el.endMeasure();
25752     },
25753
25754     /**
25755      * Sets the text for the tab (Note: this also sets the tooltip text)
25756      * @param {String} text The tab's text and tooltip
25757      */
25758     setText : function(text){
25759         this.text = text;
25760         this.textEl.update(text);
25761         this.setTooltip(text);
25762         if(!this.tabPanel.resizeTabs){
25763             this.autoSize();
25764         }
25765     },
25766     /**
25767      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25768      */
25769     activate : function(){
25770         this.tabPanel.activate(this.id);
25771     },
25772
25773     /**
25774      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25775      */
25776     disable : function(){
25777         if(this.tabPanel.active != this){
25778             this.disabled = true;
25779             this.pnode.addClass("disabled");
25780         }
25781     },
25782
25783     /**
25784      * Enables this TabPanelItem if it was previously disabled.
25785      */
25786     enable : function(){
25787         this.disabled = false;
25788         this.pnode.removeClass("disabled");
25789     },
25790
25791     /**
25792      * Sets the content for this TabPanelItem.
25793      * @param {String} content The content
25794      * @param {Boolean} loadScripts true to look for and load scripts
25795      */
25796     setContent : function(content, loadScripts){
25797         this.bodyEl.update(content, loadScripts);
25798     },
25799
25800     /**
25801      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25802      * @return {Roo.UpdateManager} The UpdateManager
25803      */
25804     getUpdateManager : function(){
25805         return this.bodyEl.getUpdateManager();
25806     },
25807
25808     /**
25809      * Set a URL to be used to load the content for this TabPanelItem.
25810      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25811      * @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)
25812      * @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)
25813      * @return {Roo.UpdateManager} The UpdateManager
25814      */
25815     setUrl : function(url, params, loadOnce){
25816         if(this.refreshDelegate){
25817             this.un('activate', this.refreshDelegate);
25818         }
25819         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25820         this.on("activate", this.refreshDelegate);
25821         return this.bodyEl.getUpdateManager();
25822     },
25823
25824     /** @private */
25825     _handleRefresh : function(url, params, loadOnce){
25826         if(!loadOnce || !this.loaded){
25827             var updater = this.bodyEl.getUpdateManager();
25828             updater.update(url, params, this._setLoaded.createDelegate(this));
25829         }
25830     },
25831
25832     /**
25833      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25834      *   Will fail silently if the setUrl method has not been called.
25835      *   This does not activate the panel, just updates its content.
25836      */
25837     refresh : function(){
25838         if(this.refreshDelegate){
25839            this.loaded = false;
25840            this.refreshDelegate();
25841         }
25842     },
25843
25844     /** @private */
25845     _setLoaded : function(){
25846         this.loaded = true;
25847     },
25848
25849     /** @private */
25850     closeClick : function(e){
25851         var o = {};
25852         e.stopEvent();
25853         this.fireEvent("beforeclose", this, o);
25854         if(o.cancel !== true){
25855             this.tabPanel.removeTab(this.id);
25856         }
25857     },
25858     /**
25859      * The text displayed in the tooltip for the close icon.
25860      * @type String
25861      */
25862     closeText : "Close this tab"
25863 });
25864
25865 /** @private */
25866 Roo.TabPanel.prototype.createStrip = function(container){
25867     var strip = document.createElement("div");
25868     strip.className = "x-tabs-wrap";
25869     container.appendChild(strip);
25870     return strip;
25871 };
25872 /** @private */
25873 Roo.TabPanel.prototype.createStripList = function(strip){
25874     // div wrapper for retard IE
25875     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25876     return strip.firstChild.firstChild.firstChild.firstChild;
25877 };
25878 /** @private */
25879 Roo.TabPanel.prototype.createBody = function(container){
25880     var body = document.createElement("div");
25881     Roo.id(body, "tab-body");
25882     Roo.fly(body).addClass("x-tabs-body");
25883     container.appendChild(body);
25884     return body;
25885 };
25886 /** @private */
25887 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25888     var body = Roo.getDom(id);
25889     if(!body){
25890         body = document.createElement("div");
25891         body.id = id;
25892     }
25893     Roo.fly(body).addClass("x-tabs-item-body");
25894     bodyEl.insertBefore(body, bodyEl.firstChild);
25895     return body;
25896 };
25897 /** @private */
25898 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25899     var td = document.createElement("td");
25900     stripEl.appendChild(td);
25901     if(closable){
25902         td.className = "x-tabs-closable";
25903         if(!this.closeTpl){
25904             this.closeTpl = new Roo.Template(
25905                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25906                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25907                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25908             );
25909         }
25910         var el = this.closeTpl.overwrite(td, {"text": text});
25911         var close = el.getElementsByTagName("div")[0];
25912         var inner = el.getElementsByTagName("em")[0];
25913         return {"el": el, "close": close, "inner": inner};
25914     } else {
25915         if(!this.tabTpl){
25916             this.tabTpl = new Roo.Template(
25917                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25918                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25919             );
25920         }
25921         var el = this.tabTpl.overwrite(td, {"text": text});
25922         var inner = el.getElementsByTagName("em")[0];
25923         return {"el": el, "inner": inner};
25924     }
25925 };/*
25926  * Based on:
25927  * Ext JS Library 1.1.1
25928  * Copyright(c) 2006-2007, Ext JS, LLC.
25929  *
25930  * Originally Released Under LGPL - original licence link has changed is not relivant.
25931  *
25932  * Fork - LGPL
25933  * <script type="text/javascript">
25934  */
25935
25936 /**
25937  * @class Roo.Button
25938  * @extends Roo.util.Observable
25939  * Simple Button class
25940  * @cfg {String} text The button text
25941  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25942  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25943  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25944  * @cfg {Object} scope The scope of the handler
25945  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25946  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25947  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25948  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25949  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25950  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25951    applies if enableToggle = true)
25952  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25953  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25954   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25955  * @constructor
25956  * Create a new button
25957  * @param {Object} config The config object
25958  */
25959 Roo.Button = function(renderTo, config)
25960 {
25961     if (!config) {
25962         config = renderTo;
25963         renderTo = config.renderTo || false;
25964     }
25965     
25966     Roo.apply(this, config);
25967     this.addEvents({
25968         /**
25969              * @event click
25970              * Fires when this button is clicked
25971              * @param {Button} this
25972              * @param {EventObject} e The click event
25973              */
25974             "click" : true,
25975         /**
25976              * @event toggle
25977              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25978              * @param {Button} this
25979              * @param {Boolean} pressed
25980              */
25981             "toggle" : true,
25982         /**
25983              * @event mouseover
25984              * Fires when the mouse hovers over the button
25985              * @param {Button} this
25986              * @param {Event} e The event object
25987              */
25988         'mouseover' : true,
25989         /**
25990              * @event mouseout
25991              * Fires when the mouse exits the button
25992              * @param {Button} this
25993              * @param {Event} e The event object
25994              */
25995         'mouseout': true,
25996          /**
25997              * @event render
25998              * Fires when the button is rendered
25999              * @param {Button} this
26000              */
26001         'render': true
26002     });
26003     if(this.menu){
26004         this.menu = Roo.menu.MenuMgr.get(this.menu);
26005     }
26006     // register listeners first!!  - so render can be captured..
26007     Roo.util.Observable.call(this);
26008     if(renderTo){
26009         this.render(renderTo);
26010     }
26011     
26012   
26013 };
26014
26015 Roo.extend(Roo.Button, Roo.util.Observable, {
26016     /**
26017      * 
26018      */
26019     
26020     /**
26021      * Read-only. True if this button is hidden
26022      * @type Boolean
26023      */
26024     hidden : false,
26025     /**
26026      * Read-only. True if this button is disabled
26027      * @type Boolean
26028      */
26029     disabled : false,
26030     /**
26031      * Read-only. True if this button is pressed (only if enableToggle = true)
26032      * @type Boolean
26033      */
26034     pressed : false,
26035
26036     /**
26037      * @cfg {Number} tabIndex 
26038      * The DOM tabIndex for this button (defaults to undefined)
26039      */
26040     tabIndex : undefined,
26041
26042     /**
26043      * @cfg {Boolean} enableToggle
26044      * True to enable pressed/not pressed toggling (defaults to false)
26045      */
26046     enableToggle: false,
26047     /**
26048      * @cfg {Mixed} menu
26049      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26050      */
26051     menu : undefined,
26052     /**
26053      * @cfg {String} menuAlign
26054      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26055      */
26056     menuAlign : "tl-bl?",
26057
26058     /**
26059      * @cfg {String} iconCls
26060      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26061      */
26062     iconCls : undefined,
26063     /**
26064      * @cfg {String} type
26065      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26066      */
26067     type : 'button',
26068
26069     // private
26070     menuClassTarget: 'tr',
26071
26072     /**
26073      * @cfg {String} clickEvent
26074      * The type of event to map to the button's event handler (defaults to 'click')
26075      */
26076     clickEvent : 'click',
26077
26078     /**
26079      * @cfg {Boolean} handleMouseEvents
26080      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26081      */
26082     handleMouseEvents : true,
26083
26084     /**
26085      * @cfg {String} tooltipType
26086      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26087      */
26088     tooltipType : 'qtip',
26089
26090     /**
26091      * @cfg {String} cls
26092      * A CSS class to apply to the button's main element.
26093      */
26094     
26095     /**
26096      * @cfg {Roo.Template} template (Optional)
26097      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26098      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26099      * require code modifications if required elements (e.g. a button) aren't present.
26100      */
26101
26102     // private
26103     render : function(renderTo){
26104         var btn;
26105         if(this.hideParent){
26106             this.parentEl = Roo.get(renderTo);
26107         }
26108         if(!this.dhconfig){
26109             if(!this.template){
26110                 if(!Roo.Button.buttonTemplate){
26111                     // hideous table template
26112                     Roo.Button.buttonTemplate = new Roo.Template(
26113                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26114                         '<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>',
26115                         "</tr></tbody></table>");
26116                 }
26117                 this.template = Roo.Button.buttonTemplate;
26118             }
26119             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26120             var btnEl = btn.child("button:first");
26121             btnEl.on('focus', this.onFocus, this);
26122             btnEl.on('blur', this.onBlur, this);
26123             if(this.cls){
26124                 btn.addClass(this.cls);
26125             }
26126             if(this.icon){
26127                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26128             }
26129             if(this.iconCls){
26130                 btnEl.addClass(this.iconCls);
26131                 if(!this.cls){
26132                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26133                 }
26134             }
26135             if(this.tabIndex !== undefined){
26136                 btnEl.dom.tabIndex = this.tabIndex;
26137             }
26138             if(this.tooltip){
26139                 if(typeof this.tooltip == 'object'){
26140                     Roo.QuickTips.tips(Roo.apply({
26141                           target: btnEl.id
26142                     }, this.tooltip));
26143                 } else {
26144                     btnEl.dom[this.tooltipType] = this.tooltip;
26145                 }
26146             }
26147         }else{
26148             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26149         }
26150         this.el = btn;
26151         if(this.id){
26152             this.el.dom.id = this.el.id = this.id;
26153         }
26154         if(this.menu){
26155             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26156             this.menu.on("show", this.onMenuShow, this);
26157             this.menu.on("hide", this.onMenuHide, this);
26158         }
26159         btn.addClass("x-btn");
26160         if(Roo.isIE && !Roo.isIE7){
26161             this.autoWidth.defer(1, this);
26162         }else{
26163             this.autoWidth();
26164         }
26165         if(this.handleMouseEvents){
26166             btn.on("mouseover", this.onMouseOver, this);
26167             btn.on("mouseout", this.onMouseOut, this);
26168             btn.on("mousedown", this.onMouseDown, this);
26169         }
26170         btn.on(this.clickEvent, this.onClick, this);
26171         //btn.on("mouseup", this.onMouseUp, this);
26172         if(this.hidden){
26173             this.hide();
26174         }
26175         if(this.disabled){
26176             this.disable();
26177         }
26178         Roo.ButtonToggleMgr.register(this);
26179         if(this.pressed){
26180             this.el.addClass("x-btn-pressed");
26181         }
26182         if(this.repeat){
26183             var repeater = new Roo.util.ClickRepeater(btn,
26184                 typeof this.repeat == "object" ? this.repeat : {}
26185             );
26186             repeater.on("click", this.onClick,  this);
26187         }
26188         
26189         this.fireEvent('render', this);
26190         
26191     },
26192     /**
26193      * Returns the button's underlying element
26194      * @return {Roo.Element} The element
26195      */
26196     getEl : function(){
26197         return this.el;  
26198     },
26199     
26200     /**
26201      * Destroys this Button and removes any listeners.
26202      */
26203     destroy : function(){
26204         Roo.ButtonToggleMgr.unregister(this);
26205         this.el.removeAllListeners();
26206         this.purgeListeners();
26207         this.el.remove();
26208     },
26209
26210     // private
26211     autoWidth : function(){
26212         if(this.el){
26213             this.el.setWidth("auto");
26214             if(Roo.isIE7 && Roo.isStrict){
26215                 var ib = this.el.child('button');
26216                 if(ib && ib.getWidth() > 20){
26217                     ib.clip();
26218                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26219                 }
26220             }
26221             if(this.minWidth){
26222                 if(this.hidden){
26223                     this.el.beginMeasure();
26224                 }
26225                 if(this.el.getWidth() < this.minWidth){
26226                     this.el.setWidth(this.minWidth);
26227                 }
26228                 if(this.hidden){
26229                     this.el.endMeasure();
26230                 }
26231             }
26232         }
26233     },
26234
26235     /**
26236      * Assigns this button's click handler
26237      * @param {Function} handler The function to call when the button is clicked
26238      * @param {Object} scope (optional) Scope for the function passed in
26239      */
26240     setHandler : function(handler, scope){
26241         this.handler = handler;
26242         this.scope = scope;  
26243     },
26244     
26245     /**
26246      * Sets this button's text
26247      * @param {String} text The button text
26248      */
26249     setText : function(text){
26250         this.text = text;
26251         if(this.el){
26252             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26253         }
26254         this.autoWidth();
26255     },
26256     
26257     /**
26258      * Gets the text for this button
26259      * @return {String} The button text
26260      */
26261     getText : function(){
26262         return this.text;  
26263     },
26264     
26265     /**
26266      * Show this button
26267      */
26268     show: function(){
26269         this.hidden = false;
26270         if(this.el){
26271             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26272         }
26273     },
26274     
26275     /**
26276      * Hide this button
26277      */
26278     hide: function(){
26279         this.hidden = true;
26280         if(this.el){
26281             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26282         }
26283     },
26284     
26285     /**
26286      * Convenience function for boolean show/hide
26287      * @param {Boolean} visible True to show, false to hide
26288      */
26289     setVisible: function(visible){
26290         if(visible) {
26291             this.show();
26292         }else{
26293             this.hide();
26294         }
26295     },
26296     
26297     /**
26298      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26299      * @param {Boolean} state (optional) Force a particular state
26300      */
26301     toggle : function(state){
26302         state = state === undefined ? !this.pressed : state;
26303         if(state != this.pressed){
26304             if(state){
26305                 this.el.addClass("x-btn-pressed");
26306                 this.pressed = true;
26307                 this.fireEvent("toggle", this, true);
26308             }else{
26309                 this.el.removeClass("x-btn-pressed");
26310                 this.pressed = false;
26311                 this.fireEvent("toggle", this, false);
26312             }
26313             if(this.toggleHandler){
26314                 this.toggleHandler.call(this.scope || this, this, state);
26315             }
26316         }
26317     },
26318     
26319     /**
26320      * Focus the button
26321      */
26322     focus : function(){
26323         this.el.child('button:first').focus();
26324     },
26325     
26326     /**
26327      * Disable this button
26328      */
26329     disable : function(){
26330         if(this.el){
26331             this.el.addClass("x-btn-disabled");
26332         }
26333         this.disabled = true;
26334     },
26335     
26336     /**
26337      * Enable this button
26338      */
26339     enable : function(){
26340         if(this.el){
26341             this.el.removeClass("x-btn-disabled");
26342         }
26343         this.disabled = false;
26344     },
26345
26346     /**
26347      * Convenience function for boolean enable/disable
26348      * @param {Boolean} enabled True to enable, false to disable
26349      */
26350     setDisabled : function(v){
26351         this[v !== true ? "enable" : "disable"]();
26352     },
26353
26354     // private
26355     onClick : function(e){
26356         if(e){
26357             e.preventDefault();
26358         }
26359         if(e.button != 0){
26360             return;
26361         }
26362         if(!this.disabled){
26363             if(this.enableToggle){
26364                 this.toggle();
26365             }
26366             if(this.menu && !this.menu.isVisible()){
26367                 this.menu.show(this.el, this.menuAlign);
26368             }
26369             this.fireEvent("click", this, e);
26370             if(this.handler){
26371                 this.el.removeClass("x-btn-over");
26372                 this.handler.call(this.scope || this, this, e);
26373             }
26374         }
26375     },
26376     // private
26377     onMouseOver : function(e){
26378         if(!this.disabled){
26379             this.el.addClass("x-btn-over");
26380             this.fireEvent('mouseover', this, e);
26381         }
26382     },
26383     // private
26384     onMouseOut : function(e){
26385         if(!e.within(this.el,  true)){
26386             this.el.removeClass("x-btn-over");
26387             this.fireEvent('mouseout', this, e);
26388         }
26389     },
26390     // private
26391     onFocus : function(e){
26392         if(!this.disabled){
26393             this.el.addClass("x-btn-focus");
26394         }
26395     },
26396     // private
26397     onBlur : function(e){
26398         this.el.removeClass("x-btn-focus");
26399     },
26400     // private
26401     onMouseDown : function(e){
26402         if(!this.disabled && e.button == 0){
26403             this.el.addClass("x-btn-click");
26404             Roo.get(document).on('mouseup', this.onMouseUp, this);
26405         }
26406     },
26407     // private
26408     onMouseUp : function(e){
26409         if(e.button == 0){
26410             this.el.removeClass("x-btn-click");
26411             Roo.get(document).un('mouseup', this.onMouseUp, this);
26412         }
26413     },
26414     // private
26415     onMenuShow : function(e){
26416         this.el.addClass("x-btn-menu-active");
26417     },
26418     // private
26419     onMenuHide : function(e){
26420         this.el.removeClass("x-btn-menu-active");
26421     }   
26422 });
26423
26424 // Private utility class used by Button
26425 Roo.ButtonToggleMgr = function(){
26426    var groups = {};
26427    
26428    function toggleGroup(btn, state){
26429        if(state){
26430            var g = groups[btn.toggleGroup];
26431            for(var i = 0, l = g.length; i < l; i++){
26432                if(g[i] != btn){
26433                    g[i].toggle(false);
26434                }
26435            }
26436        }
26437    }
26438    
26439    return {
26440        register : function(btn){
26441            if(!btn.toggleGroup){
26442                return;
26443            }
26444            var g = groups[btn.toggleGroup];
26445            if(!g){
26446                g = groups[btn.toggleGroup] = [];
26447            }
26448            g.push(btn);
26449            btn.on("toggle", toggleGroup);
26450        },
26451        
26452        unregister : function(btn){
26453            if(!btn.toggleGroup){
26454                return;
26455            }
26456            var g = groups[btn.toggleGroup];
26457            if(g){
26458                g.remove(btn);
26459                btn.un("toggle", toggleGroup);
26460            }
26461        }
26462    };
26463 }();/*
26464  * Based on:
26465  * Ext JS Library 1.1.1
26466  * Copyright(c) 2006-2007, Ext JS, LLC.
26467  *
26468  * Originally Released Under LGPL - original licence link has changed is not relivant.
26469  *
26470  * Fork - LGPL
26471  * <script type="text/javascript">
26472  */
26473  
26474 /**
26475  * @class Roo.SplitButton
26476  * @extends Roo.Button
26477  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26478  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26479  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26480  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26481  * @cfg {String} arrowTooltip The title attribute of the arrow
26482  * @constructor
26483  * Create a new menu button
26484  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26485  * @param {Object} config The config object
26486  */
26487 Roo.SplitButton = function(renderTo, config){
26488     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26489     /**
26490      * @event arrowclick
26491      * Fires when this button's arrow is clicked
26492      * @param {SplitButton} this
26493      * @param {EventObject} e The click event
26494      */
26495     this.addEvents({"arrowclick":true});
26496 };
26497
26498 Roo.extend(Roo.SplitButton, Roo.Button, {
26499     render : function(renderTo){
26500         // this is one sweet looking template!
26501         var tpl = new Roo.Template(
26502             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26503             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26504             '<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>',
26505             "</tbody></table></td><td>",
26506             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26507             '<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>',
26508             "</tbody></table></td></tr></table>"
26509         );
26510         var btn = tpl.append(renderTo, [this.text, this.type], true);
26511         var btnEl = btn.child("button");
26512         if(this.cls){
26513             btn.addClass(this.cls);
26514         }
26515         if(this.icon){
26516             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26517         }
26518         if(this.iconCls){
26519             btnEl.addClass(this.iconCls);
26520             if(!this.cls){
26521                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26522             }
26523         }
26524         this.el = btn;
26525         if(this.handleMouseEvents){
26526             btn.on("mouseover", this.onMouseOver, this);
26527             btn.on("mouseout", this.onMouseOut, this);
26528             btn.on("mousedown", this.onMouseDown, this);
26529             btn.on("mouseup", this.onMouseUp, this);
26530         }
26531         btn.on(this.clickEvent, this.onClick, this);
26532         if(this.tooltip){
26533             if(typeof this.tooltip == 'object'){
26534                 Roo.QuickTips.tips(Roo.apply({
26535                       target: btnEl.id
26536                 }, this.tooltip));
26537             } else {
26538                 btnEl.dom[this.tooltipType] = this.tooltip;
26539             }
26540         }
26541         if(this.arrowTooltip){
26542             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26543         }
26544         if(this.hidden){
26545             this.hide();
26546         }
26547         if(this.disabled){
26548             this.disable();
26549         }
26550         if(this.pressed){
26551             this.el.addClass("x-btn-pressed");
26552         }
26553         if(Roo.isIE && !Roo.isIE7){
26554             this.autoWidth.defer(1, this);
26555         }else{
26556             this.autoWidth();
26557         }
26558         if(this.menu){
26559             this.menu.on("show", this.onMenuShow, this);
26560             this.menu.on("hide", this.onMenuHide, this);
26561         }
26562         this.fireEvent('render', this);
26563     },
26564
26565     // private
26566     autoWidth : function(){
26567         if(this.el){
26568             var tbl = this.el.child("table:first");
26569             var tbl2 = this.el.child("table:last");
26570             this.el.setWidth("auto");
26571             tbl.setWidth("auto");
26572             if(Roo.isIE7 && Roo.isStrict){
26573                 var ib = this.el.child('button:first');
26574                 if(ib && ib.getWidth() > 20){
26575                     ib.clip();
26576                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26577                 }
26578             }
26579             if(this.minWidth){
26580                 if(this.hidden){
26581                     this.el.beginMeasure();
26582                 }
26583                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26584                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26585                 }
26586                 if(this.hidden){
26587                     this.el.endMeasure();
26588                 }
26589             }
26590             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26591         } 
26592     },
26593     /**
26594      * Sets this button's click handler
26595      * @param {Function} handler The function to call when the button is clicked
26596      * @param {Object} scope (optional) Scope for the function passed above
26597      */
26598     setHandler : function(handler, scope){
26599         this.handler = handler;
26600         this.scope = scope;  
26601     },
26602     
26603     /**
26604      * Sets this button's arrow click handler
26605      * @param {Function} handler The function to call when the arrow is clicked
26606      * @param {Object} scope (optional) Scope for the function passed above
26607      */
26608     setArrowHandler : function(handler, scope){
26609         this.arrowHandler = handler;
26610         this.scope = scope;  
26611     },
26612     
26613     /**
26614      * Focus the button
26615      */
26616     focus : function(){
26617         if(this.el){
26618             this.el.child("button:first").focus();
26619         }
26620     },
26621
26622     // private
26623     onClick : function(e){
26624         e.preventDefault();
26625         if(!this.disabled){
26626             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26627                 if(this.menu && !this.menu.isVisible()){
26628                     this.menu.show(this.el, this.menuAlign);
26629                 }
26630                 this.fireEvent("arrowclick", this, e);
26631                 if(this.arrowHandler){
26632                     this.arrowHandler.call(this.scope || this, this, e);
26633                 }
26634             }else{
26635                 this.fireEvent("click", this, e);
26636                 if(this.handler){
26637                     this.handler.call(this.scope || this, this, e);
26638                 }
26639             }
26640         }
26641     },
26642     // private
26643     onMouseDown : function(e){
26644         if(!this.disabled){
26645             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26646         }
26647     },
26648     // private
26649     onMouseUp : function(e){
26650         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26651     }   
26652 });
26653
26654
26655 // backwards compat
26656 Roo.MenuButton = Roo.SplitButton;/*
26657  * Based on:
26658  * Ext JS Library 1.1.1
26659  * Copyright(c) 2006-2007, Ext JS, LLC.
26660  *
26661  * Originally Released Under LGPL - original licence link has changed is not relivant.
26662  *
26663  * Fork - LGPL
26664  * <script type="text/javascript">
26665  */
26666
26667 /**
26668  * @class Roo.Toolbar
26669  * Basic Toolbar class.
26670  * @constructor
26671  * Creates a new Toolbar
26672  * @param {Object} config The config object
26673  */ 
26674 Roo.Toolbar = function(container, buttons, config)
26675 {
26676     /// old consturctor format still supported..
26677     if(container instanceof Array){ // omit the container for later rendering
26678         buttons = container;
26679         config = buttons;
26680         container = null;
26681     }
26682     if (typeof(container) == 'object' && container.xtype) {
26683         config = container;
26684         container = config.container;
26685         buttons = config.buttons; // not really - use items!!
26686     }
26687     var xitems = [];
26688     if (config && config.items) {
26689         xitems = config.items;
26690         delete config.items;
26691     }
26692     Roo.apply(this, config);
26693     this.buttons = buttons;
26694     
26695     if(container){
26696         this.render(container);
26697     }
26698     Roo.each(xitems, function(b) {
26699         this.add(b);
26700     }, this);
26701     
26702 };
26703
26704 Roo.Toolbar.prototype = {
26705     /**
26706      * @cfg {Roo.data.Store} items
26707      * array of button configs or elements to add
26708      */
26709     
26710     /**
26711      * @cfg {String/HTMLElement/Element} container
26712      * The id or element that will contain the toolbar
26713      */
26714     // private
26715     render : function(ct){
26716         this.el = Roo.get(ct);
26717         if(this.cls){
26718             this.el.addClass(this.cls);
26719         }
26720         // using a table allows for vertical alignment
26721         // 100% width is needed by Safari...
26722         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26723         this.tr = this.el.child("tr", true);
26724         var autoId = 0;
26725         this.items = new Roo.util.MixedCollection(false, function(o){
26726             return o.id || ("item" + (++autoId));
26727         });
26728         if(this.buttons){
26729             this.add.apply(this, this.buttons);
26730             delete this.buttons;
26731         }
26732     },
26733
26734     /**
26735      * Adds element(s) to the toolbar -- this function takes a variable number of 
26736      * arguments of mixed type and adds them to the toolbar.
26737      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26738      * <ul>
26739      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26740      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26741      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26742      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26743      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26744      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26745      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26746      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26747      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26748      * </ul>
26749      * @param {Mixed} arg2
26750      * @param {Mixed} etc.
26751      */
26752     add : function(){
26753         var a = arguments, l = a.length;
26754         for(var i = 0; i < l; i++){
26755             this._add(a[i]);
26756         }
26757     },
26758     // private..
26759     _add : function(el) {
26760         
26761         if (el.xtype) {
26762             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26763         }
26764         
26765         if (el.applyTo){ // some kind of form field
26766             return this.addField(el);
26767         } 
26768         if (el.render){ // some kind of Toolbar.Item
26769             return this.addItem(el);
26770         }
26771         if (typeof el == "string"){ // string
26772             if(el == "separator" || el == "-"){
26773                 return this.addSeparator();
26774             }
26775             if (el == " "){
26776                 return this.addSpacer();
26777             }
26778             if(el == "->"){
26779                 return this.addFill();
26780             }
26781             return this.addText(el);
26782             
26783         }
26784         if(el.tagName){ // element
26785             return this.addElement(el);
26786         }
26787         if(typeof el == "object"){ // must be button config?
26788             return this.addButton(el);
26789         }
26790         // and now what?!?!
26791         return false;
26792         
26793     },
26794     
26795     /**
26796      * Add an Xtype element
26797      * @param {Object} xtype Xtype Object
26798      * @return {Object} created Object
26799      */
26800     addxtype : function(e){
26801         return this.add(e);  
26802     },
26803     
26804     /**
26805      * Returns the Element for this toolbar.
26806      * @return {Roo.Element}
26807      */
26808     getEl : function(){
26809         return this.el;  
26810     },
26811     
26812     /**
26813      * Adds a separator
26814      * @return {Roo.Toolbar.Item} The separator item
26815      */
26816     addSeparator : function(){
26817         return this.addItem(new Roo.Toolbar.Separator());
26818     },
26819
26820     /**
26821      * Adds a spacer element
26822      * @return {Roo.Toolbar.Spacer} The spacer item
26823      */
26824     addSpacer : function(){
26825         return this.addItem(new Roo.Toolbar.Spacer());
26826     },
26827
26828     /**
26829      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26830      * @return {Roo.Toolbar.Fill} The fill item
26831      */
26832     addFill : function(){
26833         return this.addItem(new Roo.Toolbar.Fill());
26834     },
26835
26836     /**
26837      * Adds any standard HTML element to the toolbar
26838      * @param {String/HTMLElement/Element} el The element or id of the element to add
26839      * @return {Roo.Toolbar.Item} The element's item
26840      */
26841     addElement : function(el){
26842         return this.addItem(new Roo.Toolbar.Item(el));
26843     },
26844     /**
26845      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26846      * @type Roo.util.MixedCollection  
26847      */
26848     items : false,
26849      
26850     /**
26851      * Adds any Toolbar.Item or subclass
26852      * @param {Roo.Toolbar.Item} item
26853      * @return {Roo.Toolbar.Item} The item
26854      */
26855     addItem : function(item){
26856         var td = this.nextBlock();
26857         item.render(td);
26858         this.items.add(item);
26859         return item;
26860     },
26861     
26862     /**
26863      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26864      * @param {Object/Array} config A button config or array of configs
26865      * @return {Roo.Toolbar.Button/Array}
26866      */
26867     addButton : function(config){
26868         if(config instanceof Array){
26869             var buttons = [];
26870             for(var i = 0, len = config.length; i < len; i++) {
26871                 buttons.push(this.addButton(config[i]));
26872             }
26873             return buttons;
26874         }
26875         var b = config;
26876         if(!(config instanceof Roo.Toolbar.Button)){
26877             b = config.split ?
26878                 new Roo.Toolbar.SplitButton(config) :
26879                 new Roo.Toolbar.Button(config);
26880         }
26881         var td = this.nextBlock();
26882         b.render(td);
26883         this.items.add(b);
26884         return b;
26885     },
26886     
26887     /**
26888      * Adds text to the toolbar
26889      * @param {String} text The text to add
26890      * @return {Roo.Toolbar.Item} The element's item
26891      */
26892     addText : function(text){
26893         return this.addItem(new Roo.Toolbar.TextItem(text));
26894     },
26895     
26896     /**
26897      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26898      * @param {Number} index The index where the item is to be inserted
26899      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26900      * @return {Roo.Toolbar.Button/Item}
26901      */
26902     insertButton : function(index, item){
26903         if(item instanceof Array){
26904             var buttons = [];
26905             for(var i = 0, len = item.length; i < len; i++) {
26906                buttons.push(this.insertButton(index + i, item[i]));
26907             }
26908             return buttons;
26909         }
26910         if (!(item instanceof Roo.Toolbar.Button)){
26911            item = new Roo.Toolbar.Button(item);
26912         }
26913         var td = document.createElement("td");
26914         this.tr.insertBefore(td, this.tr.childNodes[index]);
26915         item.render(td);
26916         this.items.insert(index, item);
26917         return item;
26918     },
26919     
26920     /**
26921      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26922      * @param {Object} config
26923      * @return {Roo.Toolbar.Item} The element's item
26924      */
26925     addDom : function(config, returnEl){
26926         var td = this.nextBlock();
26927         Roo.DomHelper.overwrite(td, config);
26928         var ti = new Roo.Toolbar.Item(td.firstChild);
26929         ti.render(td);
26930         this.items.add(ti);
26931         return ti;
26932     },
26933
26934     /**
26935      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26936      * @type Roo.util.MixedCollection  
26937      */
26938     fields : false,
26939     
26940     /**
26941      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26942      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26943      * @param {Roo.form.Field} field
26944      * @return {Roo.ToolbarItem}
26945      */
26946      
26947       
26948     addField : function(field) {
26949         if (!this.fields) {
26950             var autoId = 0;
26951             this.fields = new Roo.util.MixedCollection(false, function(o){
26952                 return o.id || ("item" + (++autoId));
26953             });
26954
26955         }
26956         
26957         var td = this.nextBlock();
26958         field.render(td);
26959         var ti = new Roo.Toolbar.Item(td.firstChild);
26960         ti.render(td);
26961         this.items.add(ti);
26962         this.fields.add(field);
26963         return ti;
26964     },
26965     /**
26966      * Hide the toolbar
26967      * @method hide
26968      */
26969      
26970       
26971     hide : function()
26972     {
26973         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26974         this.el.child('div').hide();
26975     },
26976     /**
26977      * Show the toolbar
26978      * @method show
26979      */
26980     show : function()
26981     {
26982         this.el.child('div').show();
26983     },
26984       
26985     // private
26986     nextBlock : function(){
26987         var td = document.createElement("td");
26988         this.tr.appendChild(td);
26989         return td;
26990     },
26991
26992     // private
26993     destroy : function(){
26994         if(this.items){ // rendered?
26995             Roo.destroy.apply(Roo, this.items.items);
26996         }
26997         if(this.fields){ // rendered?
26998             Roo.destroy.apply(Roo, this.fields.items);
26999         }
27000         Roo.Element.uncache(this.el, this.tr);
27001     }
27002 };
27003
27004 /**
27005  * @class Roo.Toolbar.Item
27006  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27007  * @constructor
27008  * Creates a new Item
27009  * @param {HTMLElement} el 
27010  */
27011 Roo.Toolbar.Item = function(el){
27012     this.el = Roo.getDom(el);
27013     this.id = Roo.id(this.el);
27014     this.hidden = false;
27015 };
27016
27017 Roo.Toolbar.Item.prototype = {
27018     
27019     /**
27020      * Get this item's HTML Element
27021      * @return {HTMLElement}
27022      */
27023     getEl : function(){
27024        return this.el;  
27025     },
27026
27027     // private
27028     render : function(td){
27029         this.td = td;
27030         td.appendChild(this.el);
27031     },
27032     
27033     /**
27034      * Removes and destroys this item.
27035      */
27036     destroy : function(){
27037         this.td.parentNode.removeChild(this.td);
27038     },
27039     
27040     /**
27041      * Shows this item.
27042      */
27043     show: function(){
27044         this.hidden = false;
27045         this.td.style.display = "";
27046     },
27047     
27048     /**
27049      * Hides this item.
27050      */
27051     hide: function(){
27052         this.hidden = true;
27053         this.td.style.display = "none";
27054     },
27055     
27056     /**
27057      * Convenience function for boolean show/hide.
27058      * @param {Boolean} visible true to show/false to hide
27059      */
27060     setVisible: function(visible){
27061         if(visible) {
27062             this.show();
27063         }else{
27064             this.hide();
27065         }
27066     },
27067     
27068     /**
27069      * Try to focus this item.
27070      */
27071     focus : function(){
27072         Roo.fly(this.el).focus();
27073     },
27074     
27075     /**
27076      * Disables this item.
27077      */
27078     disable : function(){
27079         Roo.fly(this.td).addClass("x-item-disabled");
27080         this.disabled = true;
27081         this.el.disabled = true;
27082     },
27083     
27084     /**
27085      * Enables this item.
27086      */
27087     enable : function(){
27088         Roo.fly(this.td).removeClass("x-item-disabled");
27089         this.disabled = false;
27090         this.el.disabled = false;
27091     }
27092 };
27093
27094
27095 /**
27096  * @class Roo.Toolbar.Separator
27097  * @extends Roo.Toolbar.Item
27098  * A simple toolbar separator class
27099  * @constructor
27100  * Creates a new Separator
27101  */
27102 Roo.Toolbar.Separator = function(){
27103     var s = document.createElement("span");
27104     s.className = "ytb-sep";
27105     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27106 };
27107 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27108     enable:Roo.emptyFn,
27109     disable:Roo.emptyFn,
27110     focus:Roo.emptyFn
27111 });
27112
27113 /**
27114  * @class Roo.Toolbar.Spacer
27115  * @extends Roo.Toolbar.Item
27116  * A simple element that adds extra horizontal space to a toolbar.
27117  * @constructor
27118  * Creates a new Spacer
27119  */
27120 Roo.Toolbar.Spacer = function(){
27121     var s = document.createElement("div");
27122     s.className = "ytb-spacer";
27123     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27124 };
27125 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27126     enable:Roo.emptyFn,
27127     disable:Roo.emptyFn,
27128     focus:Roo.emptyFn
27129 });
27130
27131 /**
27132  * @class Roo.Toolbar.Fill
27133  * @extends Roo.Toolbar.Spacer
27134  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27135  * @constructor
27136  * Creates a new Spacer
27137  */
27138 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27139     // private
27140     render : function(td){
27141         td.style.width = '100%';
27142         Roo.Toolbar.Fill.superclass.render.call(this, td);
27143     }
27144 });
27145
27146 /**
27147  * @class Roo.Toolbar.TextItem
27148  * @extends Roo.Toolbar.Item
27149  * A simple class that renders text directly into a toolbar.
27150  * @constructor
27151  * Creates a new TextItem
27152  * @param {String} text
27153  */
27154 Roo.Toolbar.TextItem = function(text){
27155     if (typeof(text) == 'object') {
27156         text = text.text;
27157     }
27158     var s = document.createElement("span");
27159     s.className = "ytb-text";
27160     s.innerHTML = text;
27161     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27162 };
27163 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27164     enable:Roo.emptyFn,
27165     disable:Roo.emptyFn,
27166     focus:Roo.emptyFn
27167 });
27168
27169 /**
27170  * @class Roo.Toolbar.Button
27171  * @extends Roo.Button
27172  * A button that renders into a toolbar.
27173  * @constructor
27174  * Creates a new Button
27175  * @param {Object} config A standard {@link Roo.Button} config object
27176  */
27177 Roo.Toolbar.Button = function(config){
27178     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27179 };
27180 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27181     render : function(td){
27182         this.td = td;
27183         Roo.Toolbar.Button.superclass.render.call(this, td);
27184     },
27185     
27186     /**
27187      * Removes and destroys this button
27188      */
27189     destroy : function(){
27190         Roo.Toolbar.Button.superclass.destroy.call(this);
27191         this.td.parentNode.removeChild(this.td);
27192     },
27193     
27194     /**
27195      * Shows this button
27196      */
27197     show: function(){
27198         this.hidden = false;
27199         this.td.style.display = "";
27200     },
27201     
27202     /**
27203      * Hides this button
27204      */
27205     hide: function(){
27206         this.hidden = true;
27207         this.td.style.display = "none";
27208     },
27209
27210     /**
27211      * Disables this item
27212      */
27213     disable : function(){
27214         Roo.fly(this.td).addClass("x-item-disabled");
27215         this.disabled = true;
27216     },
27217
27218     /**
27219      * Enables this item
27220      */
27221     enable : function(){
27222         Roo.fly(this.td).removeClass("x-item-disabled");
27223         this.disabled = false;
27224     }
27225 });
27226 // backwards compat
27227 Roo.ToolbarButton = Roo.Toolbar.Button;
27228
27229 /**
27230  * @class Roo.Toolbar.SplitButton
27231  * @extends Roo.SplitButton
27232  * A menu button that renders into a toolbar.
27233  * @constructor
27234  * Creates a new SplitButton
27235  * @param {Object} config A standard {@link Roo.SplitButton} config object
27236  */
27237 Roo.Toolbar.SplitButton = function(config){
27238     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27239 };
27240 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27241     render : function(td){
27242         this.td = td;
27243         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27244     },
27245     
27246     /**
27247      * Removes and destroys this button
27248      */
27249     destroy : function(){
27250         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27251         this.td.parentNode.removeChild(this.td);
27252     },
27253     
27254     /**
27255      * Shows this button
27256      */
27257     show: function(){
27258         this.hidden = false;
27259         this.td.style.display = "";
27260     },
27261     
27262     /**
27263      * Hides this button
27264      */
27265     hide: function(){
27266         this.hidden = true;
27267         this.td.style.display = "none";
27268     }
27269 });
27270
27271 // backwards compat
27272 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27273  * Based on:
27274  * Ext JS Library 1.1.1
27275  * Copyright(c) 2006-2007, Ext JS, LLC.
27276  *
27277  * Originally Released Under LGPL - original licence link has changed is not relivant.
27278  *
27279  * Fork - LGPL
27280  * <script type="text/javascript">
27281  */
27282  
27283 /**
27284  * @class Roo.PagingToolbar
27285  * @extends Roo.Toolbar
27286  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27287  * @constructor
27288  * Create a new PagingToolbar
27289  * @param {Object} config The config object
27290  */
27291 Roo.PagingToolbar = function(el, ds, config)
27292 {
27293     // old args format still supported... - xtype is prefered..
27294     if (typeof(el) == 'object' && el.xtype) {
27295         // created from xtype...
27296         config = el;
27297         ds = el.dataSource;
27298         el = config.container;
27299     }
27300     var items = [];
27301     if (config.items) {
27302         items = config.items;
27303         config.items = [];
27304     }
27305     
27306     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27307     this.ds = ds;
27308     this.cursor = 0;
27309     this.renderButtons(this.el);
27310     this.bind(ds);
27311     
27312     // supprot items array.
27313    
27314     Roo.each(items, function(e) {
27315         this.add(Roo.factory(e));
27316     },this);
27317     
27318 };
27319
27320 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27321     /**
27322      * @cfg {Roo.data.Store} dataSource
27323      * The underlying data store providing the paged data
27324      */
27325     /**
27326      * @cfg {String/HTMLElement/Element} container
27327      * container The id or element that will contain the toolbar
27328      */
27329     /**
27330      * @cfg {Boolean} displayInfo
27331      * True to display the displayMsg (defaults to false)
27332      */
27333     /**
27334      * @cfg {Number} pageSize
27335      * The number of records to display per page (defaults to 20)
27336      */
27337     pageSize: 20,
27338     /**
27339      * @cfg {String} displayMsg
27340      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27341      */
27342     displayMsg : 'Displaying {0} - {1} of {2}',
27343     /**
27344      * @cfg {String} emptyMsg
27345      * The message to display when no records are found (defaults to "No data to display")
27346      */
27347     emptyMsg : 'No data to display',
27348     /**
27349      * Customizable piece of the default paging text (defaults to "Page")
27350      * @type String
27351      */
27352     beforePageText : "Page",
27353     /**
27354      * Customizable piece of the default paging text (defaults to "of %0")
27355      * @type String
27356      */
27357     afterPageText : "of {0}",
27358     /**
27359      * Customizable piece of the default paging text (defaults to "First Page")
27360      * @type String
27361      */
27362     firstText : "First Page",
27363     /**
27364      * Customizable piece of the default paging text (defaults to "Previous Page")
27365      * @type String
27366      */
27367     prevText : "Previous Page",
27368     /**
27369      * Customizable piece of the default paging text (defaults to "Next Page")
27370      * @type String
27371      */
27372     nextText : "Next Page",
27373     /**
27374      * Customizable piece of the default paging text (defaults to "Last Page")
27375      * @type String
27376      */
27377     lastText : "Last Page",
27378     /**
27379      * Customizable piece of the default paging text (defaults to "Refresh")
27380      * @type String
27381      */
27382     refreshText : "Refresh",
27383
27384     // private
27385     renderButtons : function(el){
27386         Roo.PagingToolbar.superclass.render.call(this, el);
27387         this.first = this.addButton({
27388             tooltip: this.firstText,
27389             cls: "x-btn-icon x-grid-page-first",
27390             disabled: true,
27391             handler: this.onClick.createDelegate(this, ["first"])
27392         });
27393         this.prev = this.addButton({
27394             tooltip: this.prevText,
27395             cls: "x-btn-icon x-grid-page-prev",
27396             disabled: true,
27397             handler: this.onClick.createDelegate(this, ["prev"])
27398         });
27399         //this.addSeparator();
27400         this.add(this.beforePageText);
27401         this.field = Roo.get(this.addDom({
27402            tag: "input",
27403            type: "text",
27404            size: "3",
27405            value: "1",
27406            cls: "x-grid-page-number"
27407         }).el);
27408         this.field.on("keydown", this.onPagingKeydown, this);
27409         this.field.on("focus", function(){this.dom.select();});
27410         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27411         this.field.setHeight(18);
27412         //this.addSeparator();
27413         this.next = this.addButton({
27414             tooltip: this.nextText,
27415             cls: "x-btn-icon x-grid-page-next",
27416             disabled: true,
27417             handler: this.onClick.createDelegate(this, ["next"])
27418         });
27419         this.last = this.addButton({
27420             tooltip: this.lastText,
27421             cls: "x-btn-icon x-grid-page-last",
27422             disabled: true,
27423             handler: this.onClick.createDelegate(this, ["last"])
27424         });
27425         //this.addSeparator();
27426         this.loading = this.addButton({
27427             tooltip: this.refreshText,
27428             cls: "x-btn-icon x-grid-loading",
27429             handler: this.onClick.createDelegate(this, ["refresh"])
27430         });
27431
27432         if(this.displayInfo){
27433             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27434         }
27435     },
27436
27437     // private
27438     updateInfo : function(){
27439         if(this.displayEl){
27440             var count = this.ds.getCount();
27441             var msg = count == 0 ?
27442                 this.emptyMsg :
27443                 String.format(
27444                     this.displayMsg,
27445                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27446                 );
27447             this.displayEl.update(msg);
27448         }
27449     },
27450
27451     // private
27452     onLoad : function(ds, r, o){
27453        this.cursor = o.params ? o.params.start : 0;
27454        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27455
27456        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27457        this.field.dom.value = ap;
27458        this.first.setDisabled(ap == 1);
27459        this.prev.setDisabled(ap == 1);
27460        this.next.setDisabled(ap == ps);
27461        this.last.setDisabled(ap == ps);
27462        this.loading.enable();
27463        this.updateInfo();
27464     },
27465
27466     // private
27467     getPageData : function(){
27468         var total = this.ds.getTotalCount();
27469         return {
27470             total : total,
27471             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27472             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27473         };
27474     },
27475
27476     // private
27477     onLoadError : function(){
27478         this.loading.enable();
27479     },
27480
27481     // private
27482     onPagingKeydown : function(e){
27483         var k = e.getKey();
27484         var d = this.getPageData();
27485         if(k == e.RETURN){
27486             var v = this.field.dom.value, pageNum;
27487             if(!v || isNaN(pageNum = parseInt(v, 10))){
27488                 this.field.dom.value = d.activePage;
27489                 return;
27490             }
27491             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27492             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27493             e.stopEvent();
27494         }
27495         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))
27496         {
27497           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27498           this.field.dom.value = pageNum;
27499           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27500           e.stopEvent();
27501         }
27502         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27503         {
27504           var v = this.field.dom.value, pageNum; 
27505           var increment = (e.shiftKey) ? 10 : 1;
27506           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27507             increment *= -1;
27508           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27509             this.field.dom.value = d.activePage;
27510             return;
27511           }
27512           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27513           {
27514             this.field.dom.value = parseInt(v, 10) + increment;
27515             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27516             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27517           }
27518           e.stopEvent();
27519         }
27520     },
27521
27522     // private
27523     beforeLoad : function(){
27524         if(this.loading){
27525             this.loading.disable();
27526         }
27527     },
27528
27529     // private
27530     onClick : function(which){
27531         var ds = this.ds;
27532         switch(which){
27533             case "first":
27534                 ds.load({params:{start: 0, limit: this.pageSize}});
27535             break;
27536             case "prev":
27537                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27538             break;
27539             case "next":
27540                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27541             break;
27542             case "last":
27543                 var total = ds.getTotalCount();
27544                 var extra = total % this.pageSize;
27545                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27546                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27547             break;
27548             case "refresh":
27549                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27550             break;
27551         }
27552     },
27553
27554     /**
27555      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27556      * @param {Roo.data.Store} store The data store to unbind
27557      */
27558     unbind : function(ds){
27559         ds.un("beforeload", this.beforeLoad, this);
27560         ds.un("load", this.onLoad, this);
27561         ds.un("loadexception", this.onLoadError, this);
27562         ds.un("remove", this.updateInfo, this);
27563         ds.un("add", this.updateInfo, this);
27564         this.ds = undefined;
27565     },
27566
27567     /**
27568      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27569      * @param {Roo.data.Store} store The data store to bind
27570      */
27571     bind : function(ds){
27572         ds.on("beforeload", this.beforeLoad, this);
27573         ds.on("load", this.onLoad, this);
27574         ds.on("loadexception", this.onLoadError, this);
27575         ds.on("remove", this.updateInfo, this);
27576         ds.on("add", this.updateInfo, this);
27577         this.ds = ds;
27578     }
27579 });/*
27580  * Based on:
27581  * Ext JS Library 1.1.1
27582  * Copyright(c) 2006-2007, Ext JS, LLC.
27583  *
27584  * Originally Released Under LGPL - original licence link has changed is not relivant.
27585  *
27586  * Fork - LGPL
27587  * <script type="text/javascript">
27588  */
27589
27590 /**
27591  * @class Roo.Resizable
27592  * @extends Roo.util.Observable
27593  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27594  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27595  * 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
27596  * the element will be wrapped for you automatically.</p>
27597  * <p>Here is the list of valid resize handles:</p>
27598  * <pre>
27599 Value   Description
27600 ------  -------------------
27601  'n'     north
27602  's'     south
27603  'e'     east
27604  'w'     west
27605  'nw'    northwest
27606  'sw'    southwest
27607  'se'    southeast
27608  'ne'    northeast
27609  'hd'    horizontal drag
27610  'all'   all
27611 </pre>
27612  * <p>Here's an example showing the creation of a typical Resizable:</p>
27613  * <pre><code>
27614 var resizer = new Roo.Resizable("element-id", {
27615     handles: 'all',
27616     minWidth: 200,
27617     minHeight: 100,
27618     maxWidth: 500,
27619     maxHeight: 400,
27620     pinned: true
27621 });
27622 resizer.on("resize", myHandler);
27623 </code></pre>
27624  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27625  * resizer.east.setDisplayed(false);</p>
27626  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27627  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27628  * resize operation's new size (defaults to [0, 0])
27629  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27630  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27631  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27632  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27633  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27634  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27635  * @cfg {Number} width The width of the element in pixels (defaults to null)
27636  * @cfg {Number} height The height of the element in pixels (defaults to null)
27637  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27638  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27639  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27640  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27641  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27642  * in favor of the handles config option (defaults to false)
27643  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27644  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27645  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27646  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27647  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27648  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27649  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27650  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27651  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27652  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27653  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27654  * @constructor
27655  * Create a new resizable component
27656  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27657  * @param {Object} config configuration options
27658   */
27659 Roo.Resizable = function(el, config)
27660 {
27661     this.el = Roo.get(el);
27662
27663     if(config && config.wrap){
27664         config.resizeChild = this.el;
27665         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27666         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27667         this.el.setStyle("overflow", "hidden");
27668         this.el.setPositioning(config.resizeChild.getPositioning());
27669         config.resizeChild.clearPositioning();
27670         if(!config.width || !config.height){
27671             var csize = config.resizeChild.getSize();
27672             this.el.setSize(csize.width, csize.height);
27673         }
27674         if(config.pinned && !config.adjustments){
27675             config.adjustments = "auto";
27676         }
27677     }
27678
27679     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27680     this.proxy.unselectable();
27681     this.proxy.enableDisplayMode('block');
27682
27683     Roo.apply(this, config);
27684
27685     if(this.pinned){
27686         this.disableTrackOver = true;
27687         this.el.addClass("x-resizable-pinned");
27688     }
27689     // if the element isn't positioned, make it relative
27690     var position = this.el.getStyle("position");
27691     if(position != "absolute" && position != "fixed"){
27692         this.el.setStyle("position", "relative");
27693     }
27694     if(!this.handles){ // no handles passed, must be legacy style
27695         this.handles = 's,e,se';
27696         if(this.multiDirectional){
27697             this.handles += ',n,w';
27698         }
27699     }
27700     if(this.handles == "all"){
27701         this.handles = "n s e w ne nw se sw";
27702     }
27703     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27704     var ps = Roo.Resizable.positions;
27705     for(var i = 0, len = hs.length; i < len; i++){
27706         if(hs[i] && ps[hs[i]]){
27707             var pos = ps[hs[i]];
27708             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27709         }
27710     }
27711     // legacy
27712     this.corner = this.southeast;
27713     
27714     // updateBox = the box can move..
27715     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27716         this.updateBox = true;
27717     }
27718
27719     this.activeHandle = null;
27720
27721     if(this.resizeChild){
27722         if(typeof this.resizeChild == "boolean"){
27723             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27724         }else{
27725             this.resizeChild = Roo.get(this.resizeChild, true);
27726         }
27727     }
27728     
27729     if(this.adjustments == "auto"){
27730         var rc = this.resizeChild;
27731         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27732         if(rc && (hw || hn)){
27733             rc.position("relative");
27734             rc.setLeft(hw ? hw.el.getWidth() : 0);
27735             rc.setTop(hn ? hn.el.getHeight() : 0);
27736         }
27737         this.adjustments = [
27738             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27739             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27740         ];
27741     }
27742
27743     if(this.draggable){
27744         this.dd = this.dynamic ?
27745             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27746         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27747     }
27748
27749     // public events
27750     this.addEvents({
27751         /**
27752          * @event beforeresize
27753          * Fired before resize is allowed. Set enabled to false to cancel resize.
27754          * @param {Roo.Resizable} this
27755          * @param {Roo.EventObject} e The mousedown event
27756          */
27757         "beforeresize" : true,
27758         /**
27759          * @event resize
27760          * Fired after a resize.
27761          * @param {Roo.Resizable} this
27762          * @param {Number} width The new width
27763          * @param {Number} height The new height
27764          * @param {Roo.EventObject} e The mouseup event
27765          */
27766         "resize" : true
27767     });
27768
27769     if(this.width !== null && this.height !== null){
27770         this.resizeTo(this.width, this.height);
27771     }else{
27772         this.updateChildSize();
27773     }
27774     if(Roo.isIE){
27775         this.el.dom.style.zoom = 1;
27776     }
27777     Roo.Resizable.superclass.constructor.call(this);
27778 };
27779
27780 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27781         resizeChild : false,
27782         adjustments : [0, 0],
27783         minWidth : 5,
27784         minHeight : 5,
27785         maxWidth : 10000,
27786         maxHeight : 10000,
27787         enabled : true,
27788         animate : false,
27789         duration : .35,
27790         dynamic : false,
27791         handles : false,
27792         multiDirectional : false,
27793         disableTrackOver : false,
27794         easing : 'easeOutStrong',
27795         widthIncrement : 0,
27796         heightIncrement : 0,
27797         pinned : false,
27798         width : null,
27799         height : null,
27800         preserveRatio : false,
27801         transparent: false,
27802         minX: 0,
27803         minY: 0,
27804         draggable: false,
27805
27806         /**
27807          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27808          */
27809         constrainTo: undefined,
27810         /**
27811          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27812          */
27813         resizeRegion: undefined,
27814
27815
27816     /**
27817      * Perform a manual resize
27818      * @param {Number} width
27819      * @param {Number} height
27820      */
27821     resizeTo : function(width, height){
27822         this.el.setSize(width, height);
27823         this.updateChildSize();
27824         this.fireEvent("resize", this, width, height, null);
27825     },
27826
27827     // private
27828     startSizing : function(e, handle){
27829         this.fireEvent("beforeresize", this, e);
27830         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27831
27832             if(!this.overlay){
27833                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27834                 this.overlay.unselectable();
27835                 this.overlay.enableDisplayMode("block");
27836                 this.overlay.on("mousemove", this.onMouseMove, this);
27837                 this.overlay.on("mouseup", this.onMouseUp, this);
27838             }
27839             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27840
27841             this.resizing = true;
27842             this.startBox = this.el.getBox();
27843             this.startPoint = e.getXY();
27844             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27845                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27846
27847             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27848             this.overlay.show();
27849
27850             if(this.constrainTo) {
27851                 var ct = Roo.get(this.constrainTo);
27852                 this.resizeRegion = ct.getRegion().adjust(
27853                     ct.getFrameWidth('t'),
27854                     ct.getFrameWidth('l'),
27855                     -ct.getFrameWidth('b'),
27856                     -ct.getFrameWidth('r')
27857                 );
27858             }
27859
27860             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27861             this.proxy.show();
27862             this.proxy.setBox(this.startBox);
27863             if(!this.dynamic){
27864                 this.proxy.setStyle('visibility', 'visible');
27865             }
27866         }
27867     },
27868
27869     // private
27870     onMouseDown : function(handle, e){
27871         if(this.enabled){
27872             e.stopEvent();
27873             this.activeHandle = handle;
27874             this.startSizing(e, handle);
27875         }
27876     },
27877
27878     // private
27879     onMouseUp : function(e){
27880         var size = this.resizeElement();
27881         this.resizing = false;
27882         this.handleOut();
27883         this.overlay.hide();
27884         this.proxy.hide();
27885         this.fireEvent("resize", this, size.width, size.height, e);
27886     },
27887
27888     // private
27889     updateChildSize : function(){
27890         if(this.resizeChild){
27891             var el = this.el;
27892             var child = this.resizeChild;
27893             var adj = this.adjustments;
27894             if(el.dom.offsetWidth){
27895                 var b = el.getSize(true);
27896                 child.setSize(b.width+adj[0], b.height+adj[1]);
27897             }
27898             // Second call here for IE
27899             // The first call enables instant resizing and
27900             // the second call corrects scroll bars if they
27901             // exist
27902             if(Roo.isIE){
27903                 setTimeout(function(){
27904                     if(el.dom.offsetWidth){
27905                         var b = el.getSize(true);
27906                         child.setSize(b.width+adj[0], b.height+adj[1]);
27907                     }
27908                 }, 10);
27909             }
27910         }
27911     },
27912
27913     // private
27914     snap : function(value, inc, min){
27915         if(!inc || !value) return value;
27916         var newValue = value;
27917         var m = value % inc;
27918         if(m > 0){
27919             if(m > (inc/2)){
27920                 newValue = value + (inc-m);
27921             }else{
27922                 newValue = value - m;
27923             }
27924         }
27925         return Math.max(min, newValue);
27926     },
27927
27928     // private
27929     resizeElement : function(){
27930         var box = this.proxy.getBox();
27931         if(this.updateBox){
27932             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27933         }else{
27934             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27935         }
27936         this.updateChildSize();
27937         if(!this.dynamic){
27938             this.proxy.hide();
27939         }
27940         return box;
27941     },
27942
27943     // private
27944     constrain : function(v, diff, m, mx){
27945         if(v - diff < m){
27946             diff = v - m;
27947         }else if(v - diff > mx){
27948             diff = mx - v;
27949         }
27950         return diff;
27951     },
27952
27953     // private
27954     onMouseMove : function(e){
27955         if(this.enabled){
27956             try{// try catch so if something goes wrong the user doesn't get hung
27957
27958             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27959                 return;
27960             }
27961
27962             //var curXY = this.startPoint;
27963             var curSize = this.curSize || this.startBox;
27964             var x = this.startBox.x, y = this.startBox.y;
27965             var ox = x, oy = y;
27966             var w = curSize.width, h = curSize.height;
27967             var ow = w, oh = h;
27968             var mw = this.minWidth, mh = this.minHeight;
27969             var mxw = this.maxWidth, mxh = this.maxHeight;
27970             var wi = this.widthIncrement;
27971             var hi = this.heightIncrement;
27972
27973             var eventXY = e.getXY();
27974             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27975             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27976
27977             var pos = this.activeHandle.position;
27978
27979             switch(pos){
27980                 case "east":
27981                     w += diffX;
27982                     w = Math.min(Math.max(mw, w), mxw);
27983                     break;
27984              
27985                 case "south":
27986                     h += diffY;
27987                     h = Math.min(Math.max(mh, h), mxh);
27988                     break;
27989                 case "southeast":
27990                     w += diffX;
27991                     h += diffY;
27992                     w = Math.min(Math.max(mw, w), mxw);
27993                     h = Math.min(Math.max(mh, h), mxh);
27994                     break;
27995                 case "north":
27996                     diffY = this.constrain(h, diffY, mh, mxh);
27997                     y += diffY;
27998                     h -= diffY;
27999                     break;
28000                 case "hdrag":
28001                     
28002                     if (wi) {
28003                         var adiffX = Math.abs(diffX);
28004                         var sub = (adiffX % wi); // how much 
28005                         if (sub > (wi/2)) { // far enough to snap
28006                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28007                         } else {
28008                             // remove difference.. 
28009                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28010                         }
28011                     }
28012                     x += diffX;
28013                     x = Math.max(this.minX, x);
28014                     break;
28015                 case "west":
28016                     diffX = this.constrain(w, diffX, mw, mxw);
28017                     x += diffX;
28018                     w -= diffX;
28019                     break;
28020                 case "northeast":
28021                     w += diffX;
28022                     w = Math.min(Math.max(mw, w), mxw);
28023                     diffY = this.constrain(h, diffY, mh, mxh);
28024                     y += diffY;
28025                     h -= diffY;
28026                     break;
28027                 case "northwest":
28028                     diffX = this.constrain(w, diffX, mw, mxw);
28029                     diffY = this.constrain(h, diffY, mh, mxh);
28030                     y += diffY;
28031                     h -= diffY;
28032                     x += diffX;
28033                     w -= diffX;
28034                     break;
28035                case "southwest":
28036                     diffX = this.constrain(w, diffX, mw, mxw);
28037                     h += diffY;
28038                     h = Math.min(Math.max(mh, h), mxh);
28039                     x += diffX;
28040                     w -= diffX;
28041                     break;
28042             }
28043
28044             var sw = this.snap(w, wi, mw);
28045             var sh = this.snap(h, hi, mh);
28046             if(sw != w || sh != h){
28047                 switch(pos){
28048                     case "northeast":
28049                         y -= sh - h;
28050                     break;
28051                     case "north":
28052                         y -= sh - h;
28053                         break;
28054                     case "southwest":
28055                         x -= sw - w;
28056                     break;
28057                     case "west":
28058                         x -= sw - w;
28059                         break;
28060                     case "northwest":
28061                         x -= sw - w;
28062                         y -= sh - h;
28063                     break;
28064                 }
28065                 w = sw;
28066                 h = sh;
28067             }
28068
28069             if(this.preserveRatio){
28070                 switch(pos){
28071                     case "southeast":
28072                     case "east":
28073                         h = oh * (w/ow);
28074                         h = Math.min(Math.max(mh, h), mxh);
28075                         w = ow * (h/oh);
28076                        break;
28077                     case "south":
28078                         w = ow * (h/oh);
28079                         w = Math.min(Math.max(mw, w), mxw);
28080                         h = oh * (w/ow);
28081                         break;
28082                     case "northeast":
28083                         w = ow * (h/oh);
28084                         w = Math.min(Math.max(mw, w), mxw);
28085                         h = oh * (w/ow);
28086                     break;
28087                     case "north":
28088                         var tw = w;
28089                         w = ow * (h/oh);
28090                         w = Math.min(Math.max(mw, w), mxw);
28091                         h = oh * (w/ow);
28092                         x += (tw - w) / 2;
28093                         break;
28094                     case "southwest":
28095                         h = oh * (w/ow);
28096                         h = Math.min(Math.max(mh, h), mxh);
28097                         var tw = w;
28098                         w = ow * (h/oh);
28099                         x += tw - w;
28100                         break;
28101                     case "west":
28102                         var th = h;
28103                         h = oh * (w/ow);
28104                         h = Math.min(Math.max(mh, h), mxh);
28105                         y += (th - h) / 2;
28106                         var tw = w;
28107                         w = ow * (h/oh);
28108                         x += tw - w;
28109                        break;
28110                     case "northwest":
28111                         var tw = w;
28112                         var th = h;
28113                         h = oh * (w/ow);
28114                         h = Math.min(Math.max(mh, h), mxh);
28115                         w = ow * (h/oh);
28116                         y += th - h;
28117                         x += tw - w;
28118                        break;
28119
28120                 }
28121             }
28122             if (pos == 'hdrag') {
28123                 w = ow;
28124             }
28125             this.proxy.setBounds(x, y, w, h);
28126             if(this.dynamic){
28127                 this.resizeElement();
28128             }
28129             }catch(e){}
28130         }
28131     },
28132
28133     // private
28134     handleOver : function(){
28135         if(this.enabled){
28136             this.el.addClass("x-resizable-over");
28137         }
28138     },
28139
28140     // private
28141     handleOut : function(){
28142         if(!this.resizing){
28143             this.el.removeClass("x-resizable-over");
28144         }
28145     },
28146
28147     /**
28148      * Returns the element this component is bound to.
28149      * @return {Roo.Element}
28150      */
28151     getEl : function(){
28152         return this.el;
28153     },
28154
28155     /**
28156      * Returns the resizeChild element (or null).
28157      * @return {Roo.Element}
28158      */
28159     getResizeChild : function(){
28160         return this.resizeChild;
28161     },
28162
28163     /**
28164      * Destroys this resizable. If the element was wrapped and
28165      * removeEl is not true then the element remains.
28166      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28167      */
28168     destroy : function(removeEl){
28169         this.proxy.remove();
28170         if(this.overlay){
28171             this.overlay.removeAllListeners();
28172             this.overlay.remove();
28173         }
28174         var ps = Roo.Resizable.positions;
28175         for(var k in ps){
28176             if(typeof ps[k] != "function" && this[ps[k]]){
28177                 var h = this[ps[k]];
28178                 h.el.removeAllListeners();
28179                 h.el.remove();
28180             }
28181         }
28182         if(removeEl){
28183             this.el.update("");
28184             this.el.remove();
28185         }
28186     }
28187 });
28188
28189 // private
28190 // hash to map config positions to true positions
28191 Roo.Resizable.positions = {
28192     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28193     hd: "hdrag"
28194 };
28195
28196 // private
28197 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28198     if(!this.tpl){
28199         // only initialize the template if resizable is used
28200         var tpl = Roo.DomHelper.createTemplate(
28201             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28202         );
28203         tpl.compile();
28204         Roo.Resizable.Handle.prototype.tpl = tpl;
28205     }
28206     this.position = pos;
28207     this.rz = rz;
28208     // show north drag fro topdra
28209     var handlepos = pos == 'hdrag' ? 'north' : pos;
28210     
28211     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28212     if (pos == 'hdrag') {
28213         this.el.setStyle('cursor', 'pointer');
28214     }
28215     this.el.unselectable();
28216     if(transparent){
28217         this.el.setOpacity(0);
28218     }
28219     this.el.on("mousedown", this.onMouseDown, this);
28220     if(!disableTrackOver){
28221         this.el.on("mouseover", this.onMouseOver, this);
28222         this.el.on("mouseout", this.onMouseOut, this);
28223     }
28224 };
28225
28226 // private
28227 Roo.Resizable.Handle.prototype = {
28228     afterResize : function(rz){
28229         // do nothing
28230     },
28231     // private
28232     onMouseDown : function(e){
28233         this.rz.onMouseDown(this, e);
28234     },
28235     // private
28236     onMouseOver : function(e){
28237         this.rz.handleOver(this, e);
28238     },
28239     // private
28240     onMouseOut : function(e){
28241         this.rz.handleOut(this, e);
28242     }
28243 };/*
28244  * Based on:
28245  * Ext JS Library 1.1.1
28246  * Copyright(c) 2006-2007, Ext JS, LLC.
28247  *
28248  * Originally Released Under LGPL - original licence link has changed is not relivant.
28249  *
28250  * Fork - LGPL
28251  * <script type="text/javascript">
28252  */
28253
28254 /**
28255  * @class Roo.Editor
28256  * @extends Roo.Component
28257  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28258  * @constructor
28259  * Create a new Editor
28260  * @param {Roo.form.Field} field The Field object (or descendant)
28261  * @param {Object} config The config object
28262  */
28263 Roo.Editor = function(field, config){
28264     Roo.Editor.superclass.constructor.call(this, config);
28265     this.field = field;
28266     this.addEvents({
28267         /**
28268              * @event beforestartedit
28269              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28270              * false from the handler of this event.
28271              * @param {Editor} this
28272              * @param {Roo.Element} boundEl The underlying element bound to this editor
28273              * @param {Mixed} value The field value being set
28274              */
28275         "beforestartedit" : true,
28276         /**
28277              * @event startedit
28278              * Fires when this editor is displayed
28279              * @param {Roo.Element} boundEl The underlying element bound to this editor
28280              * @param {Mixed} value The starting field value
28281              */
28282         "startedit" : true,
28283         /**
28284              * @event beforecomplete
28285              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28286              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28287              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28288              * event will not fire since no edit actually occurred.
28289              * @param {Editor} this
28290              * @param {Mixed} value The current field value
28291              * @param {Mixed} startValue The original field value
28292              */
28293         "beforecomplete" : true,
28294         /**
28295              * @event complete
28296              * Fires after editing is complete and any changed value has been written to the underlying field.
28297              * @param {Editor} this
28298              * @param {Mixed} value The current field value
28299              * @param {Mixed} startValue The original field value
28300              */
28301         "complete" : true,
28302         /**
28303          * @event specialkey
28304          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28305          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28306          * @param {Roo.form.Field} this
28307          * @param {Roo.EventObject} e The event object
28308          */
28309         "specialkey" : true
28310     });
28311 };
28312
28313 Roo.extend(Roo.Editor, Roo.Component, {
28314     /**
28315      * @cfg {Boolean/String} autosize
28316      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28317      * or "height" to adopt the height only (defaults to false)
28318      */
28319     /**
28320      * @cfg {Boolean} revertInvalid
28321      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28322      * validation fails (defaults to true)
28323      */
28324     /**
28325      * @cfg {Boolean} ignoreNoChange
28326      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28327      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28328      * will never be ignored.
28329      */
28330     /**
28331      * @cfg {Boolean} hideEl
28332      * False to keep the bound element visible while the editor is displayed (defaults to true)
28333      */
28334     /**
28335      * @cfg {Mixed} value
28336      * The data value of the underlying field (defaults to "")
28337      */
28338     value : "",
28339     /**
28340      * @cfg {String} alignment
28341      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28342      */
28343     alignment: "c-c?",
28344     /**
28345      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28346      * for bottom-right shadow (defaults to "frame")
28347      */
28348     shadow : "frame",
28349     /**
28350      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28351      */
28352     constrain : false,
28353     /**
28354      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28355      */
28356     completeOnEnter : false,
28357     /**
28358      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28359      */
28360     cancelOnEsc : false,
28361     /**
28362      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28363      */
28364     updateEl : false,
28365
28366     // private
28367     onRender : function(ct, position){
28368         this.el = new Roo.Layer({
28369             shadow: this.shadow,
28370             cls: "x-editor",
28371             parentEl : ct,
28372             shim : this.shim,
28373             shadowOffset:4,
28374             id: this.id,
28375             constrain: this.constrain
28376         });
28377         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28378         if(this.field.msgTarget != 'title'){
28379             this.field.msgTarget = 'qtip';
28380         }
28381         this.field.render(this.el);
28382         if(Roo.isGecko){
28383             this.field.el.dom.setAttribute('autocomplete', 'off');
28384         }
28385         this.field.on("specialkey", this.onSpecialKey, this);
28386         if(this.swallowKeys){
28387             this.field.el.swallowEvent(['keydown','keypress']);
28388         }
28389         this.field.show();
28390         this.field.on("blur", this.onBlur, this);
28391         if(this.field.grow){
28392             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28393         }
28394     },
28395
28396     onSpecialKey : function(field, e)
28397     {
28398         //Roo.log('editor onSpecialKey');
28399         if(this.completeOnEnter && e.getKey() == e.ENTER){
28400             e.stopEvent();
28401             this.completeEdit();
28402             return;
28403         }
28404         // do not fire special key otherwise it might hide close the editor...
28405         if(e.getKey() == e.ENTER){    
28406             return;
28407         }
28408         if(this.cancelOnEsc && e.getKey() == e.ESC){
28409             this.cancelEdit();
28410             return;
28411         } 
28412         this.fireEvent('specialkey', field, e);
28413     
28414     },
28415
28416     /**
28417      * Starts the editing process and shows the editor.
28418      * @param {String/HTMLElement/Element} el The element to edit
28419      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28420       * to the innerHTML of el.
28421      */
28422     startEdit : function(el, value){
28423         if(this.editing){
28424             this.completeEdit();
28425         }
28426         this.boundEl = Roo.get(el);
28427         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28428         if(!this.rendered){
28429             this.render(this.parentEl || document.body);
28430         }
28431         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28432             return;
28433         }
28434         this.startValue = v;
28435         this.field.setValue(v);
28436         if(this.autoSize){
28437             var sz = this.boundEl.getSize();
28438             switch(this.autoSize){
28439                 case "width":
28440                 this.setSize(sz.width,  "");
28441                 break;
28442                 case "height":
28443                 this.setSize("",  sz.height);
28444                 break;
28445                 default:
28446                 this.setSize(sz.width,  sz.height);
28447             }
28448         }
28449         this.el.alignTo(this.boundEl, this.alignment);
28450         this.editing = true;
28451         if(Roo.QuickTips){
28452             Roo.QuickTips.disable();
28453         }
28454         this.show();
28455     },
28456
28457     /**
28458      * Sets the height and width of this editor.
28459      * @param {Number} width The new width
28460      * @param {Number} height The new height
28461      */
28462     setSize : function(w, h){
28463         this.field.setSize(w, h);
28464         if(this.el){
28465             this.el.sync();
28466         }
28467     },
28468
28469     /**
28470      * Realigns the editor to the bound field based on the current alignment config value.
28471      */
28472     realign : function(){
28473         this.el.alignTo(this.boundEl, this.alignment);
28474     },
28475
28476     /**
28477      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28478      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28479      */
28480     completeEdit : function(remainVisible){
28481         if(!this.editing){
28482             return;
28483         }
28484         var v = this.getValue();
28485         if(this.revertInvalid !== false && !this.field.isValid()){
28486             v = this.startValue;
28487             this.cancelEdit(true);
28488         }
28489         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28490             this.editing = false;
28491             this.hide();
28492             return;
28493         }
28494         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28495             this.editing = false;
28496             if(this.updateEl && this.boundEl){
28497                 this.boundEl.update(v);
28498             }
28499             if(remainVisible !== true){
28500                 this.hide();
28501             }
28502             this.fireEvent("complete", this, v, this.startValue);
28503         }
28504     },
28505
28506     // private
28507     onShow : function(){
28508         this.el.show();
28509         if(this.hideEl !== false){
28510             this.boundEl.hide();
28511         }
28512         this.field.show();
28513         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28514             this.fixIEFocus = true;
28515             this.deferredFocus.defer(50, this);
28516         }else{
28517             this.field.focus();
28518         }
28519         this.fireEvent("startedit", this.boundEl, this.startValue);
28520     },
28521
28522     deferredFocus : function(){
28523         if(this.editing){
28524             this.field.focus();
28525         }
28526     },
28527
28528     /**
28529      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28530      * reverted to the original starting value.
28531      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28532      * cancel (defaults to false)
28533      */
28534     cancelEdit : function(remainVisible){
28535         if(this.editing){
28536             this.setValue(this.startValue);
28537             if(remainVisible !== true){
28538                 this.hide();
28539             }
28540         }
28541     },
28542
28543     // private
28544     onBlur : function(){
28545         if(this.allowBlur !== true && this.editing){
28546             this.completeEdit();
28547         }
28548     },
28549
28550     // private
28551     onHide : function(){
28552         if(this.editing){
28553             this.completeEdit();
28554             return;
28555         }
28556         this.field.blur();
28557         if(this.field.collapse){
28558             this.field.collapse();
28559         }
28560         this.el.hide();
28561         if(this.hideEl !== false){
28562             this.boundEl.show();
28563         }
28564         if(Roo.QuickTips){
28565             Roo.QuickTips.enable();
28566         }
28567     },
28568
28569     /**
28570      * Sets the data value of the editor
28571      * @param {Mixed} value Any valid value supported by the underlying field
28572      */
28573     setValue : function(v){
28574         this.field.setValue(v);
28575     },
28576
28577     /**
28578      * Gets the data value of the editor
28579      * @return {Mixed} The data value
28580      */
28581     getValue : function(){
28582         return this.field.getValue();
28583     }
28584 });/*
28585  * Based on:
28586  * Ext JS Library 1.1.1
28587  * Copyright(c) 2006-2007, Ext JS, LLC.
28588  *
28589  * Originally Released Under LGPL - original licence link has changed is not relivant.
28590  *
28591  * Fork - LGPL
28592  * <script type="text/javascript">
28593  */
28594  
28595 /**
28596  * @class Roo.BasicDialog
28597  * @extends Roo.util.Observable
28598  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28599  * <pre><code>
28600 var dlg = new Roo.BasicDialog("my-dlg", {
28601     height: 200,
28602     width: 300,
28603     minHeight: 100,
28604     minWidth: 150,
28605     modal: true,
28606     proxyDrag: true,
28607     shadow: true
28608 });
28609 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28610 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28611 dlg.addButton('Cancel', dlg.hide, dlg);
28612 dlg.show();
28613 </code></pre>
28614   <b>A Dialog should always be a direct child of the body element.</b>
28615  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28616  * @cfg {String} title Default text to display in the title bar (defaults to null)
28617  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28618  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28619  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28620  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28621  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28622  * (defaults to null with no animation)
28623  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28624  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28625  * property for valid values (defaults to 'all')
28626  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28627  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28628  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28629  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28630  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28631  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28632  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28633  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28634  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28635  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28636  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28637  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28638  * draggable = true (defaults to false)
28639  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28640  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28641  * shadow (defaults to false)
28642  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28643  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28644  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28645  * @cfg {Array} buttons Array of buttons
28646  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28647  * @constructor
28648  * Create a new BasicDialog.
28649  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28650  * @param {Object} config Configuration options
28651  */
28652 Roo.BasicDialog = function(el, config){
28653     this.el = Roo.get(el);
28654     var dh = Roo.DomHelper;
28655     if(!this.el && config && config.autoCreate){
28656         if(typeof config.autoCreate == "object"){
28657             if(!config.autoCreate.id){
28658                 config.autoCreate.id = el;
28659             }
28660             this.el = dh.append(document.body,
28661                         config.autoCreate, true);
28662         }else{
28663             this.el = dh.append(document.body,
28664                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28665         }
28666     }
28667     el = this.el;
28668     el.setDisplayed(true);
28669     el.hide = this.hideAction;
28670     this.id = el.id;
28671     el.addClass("x-dlg");
28672
28673     Roo.apply(this, config);
28674
28675     this.proxy = el.createProxy("x-dlg-proxy");
28676     this.proxy.hide = this.hideAction;
28677     this.proxy.setOpacity(.5);
28678     this.proxy.hide();
28679
28680     if(config.width){
28681         el.setWidth(config.width);
28682     }
28683     if(config.height){
28684         el.setHeight(config.height);
28685     }
28686     this.size = el.getSize();
28687     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28688         this.xy = [config.x,config.y];
28689     }else{
28690         this.xy = el.getCenterXY(true);
28691     }
28692     /** The header element @type Roo.Element */
28693     this.header = el.child("> .x-dlg-hd");
28694     /** The body element @type Roo.Element */
28695     this.body = el.child("> .x-dlg-bd");
28696     /** The footer element @type Roo.Element */
28697     this.footer = el.child("> .x-dlg-ft");
28698
28699     if(!this.header){
28700         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28701     }
28702     if(!this.body){
28703         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28704     }
28705
28706     this.header.unselectable();
28707     if(this.title){
28708         this.header.update(this.title);
28709     }
28710     // this element allows the dialog to be focused for keyboard event
28711     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28712     this.focusEl.swallowEvent("click", true);
28713
28714     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28715
28716     // wrap the body and footer for special rendering
28717     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28718     if(this.footer){
28719         this.bwrap.dom.appendChild(this.footer.dom);
28720     }
28721
28722     this.bg = this.el.createChild({
28723         tag: "div", cls:"x-dlg-bg",
28724         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28725     });
28726     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28727
28728
28729     if(this.autoScroll !== false && !this.autoTabs){
28730         this.body.setStyle("overflow", "auto");
28731     }
28732
28733     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28734
28735     if(this.closable !== false){
28736         this.el.addClass("x-dlg-closable");
28737         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28738         this.close.on("click", this.closeClick, this);
28739         this.close.addClassOnOver("x-dlg-close-over");
28740     }
28741     if(this.collapsible !== false){
28742         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28743         this.collapseBtn.on("click", this.collapseClick, this);
28744         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28745         this.header.on("dblclick", this.collapseClick, this);
28746     }
28747     if(this.resizable !== false){
28748         this.el.addClass("x-dlg-resizable");
28749         this.resizer = new Roo.Resizable(el, {
28750             minWidth: this.minWidth || 80,
28751             minHeight:this.minHeight || 80,
28752             handles: this.resizeHandles || "all",
28753             pinned: true
28754         });
28755         this.resizer.on("beforeresize", this.beforeResize, this);
28756         this.resizer.on("resize", this.onResize, this);
28757     }
28758     if(this.draggable !== false){
28759         el.addClass("x-dlg-draggable");
28760         if (!this.proxyDrag) {
28761             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28762         }
28763         else {
28764             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28765         }
28766         dd.setHandleElId(this.header.id);
28767         dd.endDrag = this.endMove.createDelegate(this);
28768         dd.startDrag = this.startMove.createDelegate(this);
28769         dd.onDrag = this.onDrag.createDelegate(this);
28770         dd.scroll = false;
28771         this.dd = dd;
28772     }
28773     if(this.modal){
28774         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28775         this.mask.enableDisplayMode("block");
28776         this.mask.hide();
28777         this.el.addClass("x-dlg-modal");
28778     }
28779     if(this.shadow){
28780         this.shadow = new Roo.Shadow({
28781             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28782             offset : this.shadowOffset
28783         });
28784     }else{
28785         this.shadowOffset = 0;
28786     }
28787     if(Roo.useShims && this.shim !== false){
28788         this.shim = this.el.createShim();
28789         this.shim.hide = this.hideAction;
28790         this.shim.hide();
28791     }else{
28792         this.shim = false;
28793     }
28794     if(this.autoTabs){
28795         this.initTabs();
28796     }
28797     if (this.buttons) { 
28798         var bts= this.buttons;
28799         this.buttons = [];
28800         Roo.each(bts, function(b) {
28801             this.addButton(b);
28802         }, this);
28803     }
28804     
28805     
28806     this.addEvents({
28807         /**
28808          * @event keydown
28809          * Fires when a key is pressed
28810          * @param {Roo.BasicDialog} this
28811          * @param {Roo.EventObject} e
28812          */
28813         "keydown" : true,
28814         /**
28815          * @event move
28816          * Fires when this dialog is moved by the user.
28817          * @param {Roo.BasicDialog} this
28818          * @param {Number} x The new page X
28819          * @param {Number} y The new page Y
28820          */
28821         "move" : true,
28822         /**
28823          * @event resize
28824          * Fires when this dialog is resized by the user.
28825          * @param {Roo.BasicDialog} this
28826          * @param {Number} width The new width
28827          * @param {Number} height The new height
28828          */
28829         "resize" : true,
28830         /**
28831          * @event beforehide
28832          * Fires before this dialog is hidden.
28833          * @param {Roo.BasicDialog} this
28834          */
28835         "beforehide" : true,
28836         /**
28837          * @event hide
28838          * Fires when this dialog is hidden.
28839          * @param {Roo.BasicDialog} this
28840          */
28841         "hide" : true,
28842         /**
28843          * @event beforeshow
28844          * Fires before this dialog is shown.
28845          * @param {Roo.BasicDialog} this
28846          */
28847         "beforeshow" : true,
28848         /**
28849          * @event show
28850          * Fires when this dialog is shown.
28851          * @param {Roo.BasicDialog} this
28852          */
28853         "show" : true
28854     });
28855     el.on("keydown", this.onKeyDown, this);
28856     el.on("mousedown", this.toFront, this);
28857     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28858     this.el.hide();
28859     Roo.DialogManager.register(this);
28860     Roo.BasicDialog.superclass.constructor.call(this);
28861 };
28862
28863 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28864     shadowOffset: Roo.isIE ? 6 : 5,
28865     minHeight: 80,
28866     minWidth: 200,
28867     minButtonWidth: 75,
28868     defaultButton: null,
28869     buttonAlign: "right",
28870     tabTag: 'div',
28871     firstShow: true,
28872
28873     /**
28874      * Sets the dialog title text
28875      * @param {String} text The title text to display
28876      * @return {Roo.BasicDialog} this
28877      */
28878     setTitle : function(text){
28879         this.header.update(text);
28880         return this;
28881     },
28882
28883     // private
28884     closeClick : function(){
28885         this.hide();
28886     },
28887
28888     // private
28889     collapseClick : function(){
28890         this[this.collapsed ? "expand" : "collapse"]();
28891     },
28892
28893     /**
28894      * Collapses the dialog to its minimized state (only the title bar is visible).
28895      * Equivalent to the user clicking the collapse dialog button.
28896      */
28897     collapse : function(){
28898         if(!this.collapsed){
28899             this.collapsed = true;
28900             this.el.addClass("x-dlg-collapsed");
28901             this.restoreHeight = this.el.getHeight();
28902             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28903         }
28904     },
28905
28906     /**
28907      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28908      * clicking the expand dialog button.
28909      */
28910     expand : function(){
28911         if(this.collapsed){
28912             this.collapsed = false;
28913             this.el.removeClass("x-dlg-collapsed");
28914             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28915         }
28916     },
28917
28918     /**
28919      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28920      * @return {Roo.TabPanel} The tabs component
28921      */
28922     initTabs : function(){
28923         var tabs = this.getTabs();
28924         while(tabs.getTab(0)){
28925             tabs.removeTab(0);
28926         }
28927         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28928             var dom = el.dom;
28929             tabs.addTab(Roo.id(dom), dom.title);
28930             dom.title = "";
28931         });
28932         tabs.activate(0);
28933         return tabs;
28934     },
28935
28936     // private
28937     beforeResize : function(){
28938         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28939     },
28940
28941     // private
28942     onResize : function(){
28943         this.refreshSize();
28944         this.syncBodyHeight();
28945         this.adjustAssets();
28946         this.focus();
28947         this.fireEvent("resize", this, this.size.width, this.size.height);
28948     },
28949
28950     // private
28951     onKeyDown : function(e){
28952         if(this.isVisible()){
28953             this.fireEvent("keydown", this, e);
28954         }
28955     },
28956
28957     /**
28958      * Resizes the dialog.
28959      * @param {Number} width
28960      * @param {Number} height
28961      * @return {Roo.BasicDialog} this
28962      */
28963     resizeTo : function(width, height){
28964         this.el.setSize(width, height);
28965         this.size = {width: width, height: height};
28966         this.syncBodyHeight();
28967         if(this.fixedcenter){
28968             this.center();
28969         }
28970         if(this.isVisible()){
28971             this.constrainXY();
28972             this.adjustAssets();
28973         }
28974         this.fireEvent("resize", this, width, height);
28975         return this;
28976     },
28977
28978
28979     /**
28980      * Resizes the dialog to fit the specified content size.
28981      * @param {Number} width
28982      * @param {Number} height
28983      * @return {Roo.BasicDialog} this
28984      */
28985     setContentSize : function(w, h){
28986         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28987         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28988         //if(!this.el.isBorderBox()){
28989             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28990             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28991         //}
28992         if(this.tabs){
28993             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28994             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28995         }
28996         this.resizeTo(w, h);
28997         return this;
28998     },
28999
29000     /**
29001      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29002      * executed in response to a particular key being pressed while the dialog is active.
29003      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29004      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29005      * @param {Function} fn The function to call
29006      * @param {Object} scope (optional) The scope of the function
29007      * @return {Roo.BasicDialog} this
29008      */
29009     addKeyListener : function(key, fn, scope){
29010         var keyCode, shift, ctrl, alt;
29011         if(typeof key == "object" && !(key instanceof Array)){
29012             keyCode = key["key"];
29013             shift = key["shift"];
29014             ctrl = key["ctrl"];
29015             alt = key["alt"];
29016         }else{
29017             keyCode = key;
29018         }
29019         var handler = function(dlg, e){
29020             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29021                 var k = e.getKey();
29022                 if(keyCode instanceof Array){
29023                     for(var i = 0, len = keyCode.length; i < len; i++){
29024                         if(keyCode[i] == k){
29025                           fn.call(scope || window, dlg, k, e);
29026                           return;
29027                         }
29028                     }
29029                 }else{
29030                     if(k == keyCode){
29031                         fn.call(scope || window, dlg, k, e);
29032                     }
29033                 }
29034             }
29035         };
29036         this.on("keydown", handler);
29037         return this;
29038     },
29039
29040     /**
29041      * Returns the TabPanel component (creates it if it doesn't exist).
29042      * Note: If you wish to simply check for the existence of tabs without creating them,
29043      * check for a null 'tabs' property.
29044      * @return {Roo.TabPanel} The tabs component
29045      */
29046     getTabs : function(){
29047         if(!this.tabs){
29048             this.el.addClass("x-dlg-auto-tabs");
29049             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29050             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29051         }
29052         return this.tabs;
29053     },
29054
29055     /**
29056      * Adds a button to the footer section of the dialog.
29057      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29058      * object or a valid Roo.DomHelper element config
29059      * @param {Function} handler The function called when the button is clicked
29060      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29061      * @return {Roo.Button} The new button
29062      */
29063     addButton : function(config, handler, scope){
29064         var dh = Roo.DomHelper;
29065         if(!this.footer){
29066             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29067         }
29068         if(!this.btnContainer){
29069             var tb = this.footer.createChild({
29070
29071                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29072                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29073             }, null, true);
29074             this.btnContainer = tb.firstChild.firstChild.firstChild;
29075         }
29076         var bconfig = {
29077             handler: handler,
29078             scope: scope,
29079             minWidth: this.minButtonWidth,
29080             hideParent:true
29081         };
29082         if(typeof config == "string"){
29083             bconfig.text = config;
29084         }else{
29085             if(config.tag){
29086                 bconfig.dhconfig = config;
29087             }else{
29088                 Roo.apply(bconfig, config);
29089             }
29090         }
29091         var fc = false;
29092         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29093             bconfig.position = Math.max(0, bconfig.position);
29094             fc = this.btnContainer.childNodes[bconfig.position];
29095         }
29096          
29097         var btn = new Roo.Button(
29098             fc ? 
29099                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29100                 : this.btnContainer.appendChild(document.createElement("td")),
29101             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29102             bconfig
29103         );
29104         this.syncBodyHeight();
29105         if(!this.buttons){
29106             /**
29107              * Array of all the buttons that have been added to this dialog via addButton
29108              * @type Array
29109              */
29110             this.buttons = [];
29111         }
29112         this.buttons.push(btn);
29113         return btn;
29114     },
29115
29116     /**
29117      * Sets the default button to be focused when the dialog is displayed.
29118      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29119      * @return {Roo.BasicDialog} this
29120      */
29121     setDefaultButton : function(btn){
29122         this.defaultButton = btn;
29123         return this;
29124     },
29125
29126     // private
29127     getHeaderFooterHeight : function(safe){
29128         var height = 0;
29129         if(this.header){
29130            height += this.header.getHeight();
29131         }
29132         if(this.footer){
29133            var fm = this.footer.getMargins();
29134             height += (this.footer.getHeight()+fm.top+fm.bottom);
29135         }
29136         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29137         height += this.centerBg.getPadding("tb");
29138         return height;
29139     },
29140
29141     // private
29142     syncBodyHeight : function(){
29143         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29144         var height = this.size.height - this.getHeaderFooterHeight(false);
29145         bd.setHeight(height-bd.getMargins("tb"));
29146         var hh = this.header.getHeight();
29147         var h = this.size.height-hh;
29148         cb.setHeight(h);
29149         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29150         bw.setHeight(h-cb.getPadding("tb"));
29151         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29152         bd.setWidth(bw.getWidth(true));
29153         if(this.tabs){
29154             this.tabs.syncHeight();
29155             if(Roo.isIE){
29156                 this.tabs.el.repaint();
29157             }
29158         }
29159     },
29160
29161     /**
29162      * Restores the previous state of the dialog if Roo.state is configured.
29163      * @return {Roo.BasicDialog} this
29164      */
29165     restoreState : function(){
29166         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29167         if(box && box.width){
29168             this.xy = [box.x, box.y];
29169             this.resizeTo(box.width, box.height);
29170         }
29171         return this;
29172     },
29173
29174     // private
29175     beforeShow : function(){
29176         this.expand();
29177         if(this.fixedcenter){
29178             this.xy = this.el.getCenterXY(true);
29179         }
29180         if(this.modal){
29181             Roo.get(document.body).addClass("x-body-masked");
29182             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29183             this.mask.show();
29184         }
29185         this.constrainXY();
29186     },
29187
29188     // private
29189     animShow : function(){
29190         var b = Roo.get(this.animateTarget).getBox();
29191         this.proxy.setSize(b.width, b.height);
29192         this.proxy.setLocation(b.x, b.y);
29193         this.proxy.show();
29194         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29195                     true, .35, this.showEl.createDelegate(this));
29196     },
29197
29198     /**
29199      * Shows the dialog.
29200      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29201      * @return {Roo.BasicDialog} this
29202      */
29203     show : function(animateTarget){
29204         if (this.fireEvent("beforeshow", this) === false){
29205             return;
29206         }
29207         if(this.syncHeightBeforeShow){
29208             this.syncBodyHeight();
29209         }else if(this.firstShow){
29210             this.firstShow = false;
29211             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29212         }
29213         this.animateTarget = animateTarget || this.animateTarget;
29214         if(!this.el.isVisible()){
29215             this.beforeShow();
29216             if(this.animateTarget && Roo.get(this.animateTarget)){
29217                 this.animShow();
29218             }else{
29219                 this.showEl();
29220             }
29221         }
29222         return this;
29223     },
29224
29225     // private
29226     showEl : function(){
29227         this.proxy.hide();
29228         this.el.setXY(this.xy);
29229         this.el.show();
29230         this.adjustAssets(true);
29231         this.toFront();
29232         this.focus();
29233         // IE peekaboo bug - fix found by Dave Fenwick
29234         if(Roo.isIE){
29235             this.el.repaint();
29236         }
29237         this.fireEvent("show", this);
29238     },
29239
29240     /**
29241      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29242      * dialog itself will receive focus.
29243      */
29244     focus : function(){
29245         if(this.defaultButton){
29246             this.defaultButton.focus();
29247         }else{
29248             this.focusEl.focus();
29249         }
29250     },
29251
29252     // private
29253     constrainXY : function(){
29254         if(this.constraintoviewport !== false){
29255             if(!this.viewSize){
29256                 if(this.container){
29257                     var s = this.container.getSize();
29258                     this.viewSize = [s.width, s.height];
29259                 }else{
29260                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29261                 }
29262             }
29263             var s = Roo.get(this.container||document).getScroll();
29264
29265             var x = this.xy[0], y = this.xy[1];
29266             var w = this.size.width, h = this.size.height;
29267             var vw = this.viewSize[0], vh = this.viewSize[1];
29268             // only move it if it needs it
29269             var moved = false;
29270             // first validate right/bottom
29271             if(x + w > vw+s.left){
29272                 x = vw - w;
29273                 moved = true;
29274             }
29275             if(y + h > vh+s.top){
29276                 y = vh - h;
29277                 moved = true;
29278             }
29279             // then make sure top/left isn't negative
29280             if(x < s.left){
29281                 x = s.left;
29282                 moved = true;
29283             }
29284             if(y < s.top){
29285                 y = s.top;
29286                 moved = true;
29287             }
29288             if(moved){
29289                 // cache xy
29290                 this.xy = [x, y];
29291                 if(this.isVisible()){
29292                     this.el.setLocation(x, y);
29293                     this.adjustAssets();
29294                 }
29295             }
29296         }
29297     },
29298
29299     // private
29300     onDrag : function(){
29301         if(!this.proxyDrag){
29302             this.xy = this.el.getXY();
29303             this.adjustAssets();
29304         }
29305     },
29306
29307     // private
29308     adjustAssets : function(doShow){
29309         var x = this.xy[0], y = this.xy[1];
29310         var w = this.size.width, h = this.size.height;
29311         if(doShow === true){
29312             if(this.shadow){
29313                 this.shadow.show(this.el);
29314             }
29315             if(this.shim){
29316                 this.shim.show();
29317             }
29318         }
29319         if(this.shadow && this.shadow.isVisible()){
29320             this.shadow.show(this.el);
29321         }
29322         if(this.shim && this.shim.isVisible()){
29323             this.shim.setBounds(x, y, w, h);
29324         }
29325     },
29326
29327     // private
29328     adjustViewport : function(w, h){
29329         if(!w || !h){
29330             w = Roo.lib.Dom.getViewWidth();
29331             h = Roo.lib.Dom.getViewHeight();
29332         }
29333         // cache the size
29334         this.viewSize = [w, h];
29335         if(this.modal && this.mask.isVisible()){
29336             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29337             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29338         }
29339         if(this.isVisible()){
29340             this.constrainXY();
29341         }
29342     },
29343
29344     /**
29345      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29346      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29347      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29348      */
29349     destroy : function(removeEl){
29350         if(this.isVisible()){
29351             this.animateTarget = null;
29352             this.hide();
29353         }
29354         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29355         if(this.tabs){
29356             this.tabs.destroy(removeEl);
29357         }
29358         Roo.destroy(
29359              this.shim,
29360              this.proxy,
29361              this.resizer,
29362              this.close,
29363              this.mask
29364         );
29365         if(this.dd){
29366             this.dd.unreg();
29367         }
29368         if(this.buttons){
29369            for(var i = 0, len = this.buttons.length; i < len; i++){
29370                this.buttons[i].destroy();
29371            }
29372         }
29373         this.el.removeAllListeners();
29374         if(removeEl === true){
29375             this.el.update("");
29376             this.el.remove();
29377         }
29378         Roo.DialogManager.unregister(this);
29379     },
29380
29381     // private
29382     startMove : function(){
29383         if(this.proxyDrag){
29384             this.proxy.show();
29385         }
29386         if(this.constraintoviewport !== false){
29387             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29388         }
29389     },
29390
29391     // private
29392     endMove : function(){
29393         if(!this.proxyDrag){
29394             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29395         }else{
29396             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29397             this.proxy.hide();
29398         }
29399         this.refreshSize();
29400         this.adjustAssets();
29401         this.focus();
29402         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29403     },
29404
29405     /**
29406      * Brings this dialog to the front of any other visible dialogs
29407      * @return {Roo.BasicDialog} this
29408      */
29409     toFront : function(){
29410         Roo.DialogManager.bringToFront(this);
29411         return this;
29412     },
29413
29414     /**
29415      * Sends this dialog to the back (under) of any other visible dialogs
29416      * @return {Roo.BasicDialog} this
29417      */
29418     toBack : function(){
29419         Roo.DialogManager.sendToBack(this);
29420         return this;
29421     },
29422
29423     /**
29424      * Centers this dialog in the viewport
29425      * @return {Roo.BasicDialog} this
29426      */
29427     center : function(){
29428         var xy = this.el.getCenterXY(true);
29429         this.moveTo(xy[0], xy[1]);
29430         return this;
29431     },
29432
29433     /**
29434      * Moves the dialog's top-left corner to the specified point
29435      * @param {Number} x
29436      * @param {Number} y
29437      * @return {Roo.BasicDialog} this
29438      */
29439     moveTo : function(x, y){
29440         this.xy = [x,y];
29441         if(this.isVisible()){
29442             this.el.setXY(this.xy);
29443             this.adjustAssets();
29444         }
29445         return this;
29446     },
29447
29448     /**
29449      * Aligns the dialog to the specified element
29450      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29451      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29452      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29453      * @return {Roo.BasicDialog} this
29454      */
29455     alignTo : function(element, position, offsets){
29456         this.xy = this.el.getAlignToXY(element, position, offsets);
29457         if(this.isVisible()){
29458             this.el.setXY(this.xy);
29459             this.adjustAssets();
29460         }
29461         return this;
29462     },
29463
29464     /**
29465      * Anchors an element to another element and realigns it when the window is resized.
29466      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29467      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29468      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29469      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29470      * is a number, it is used as the buffer delay (defaults to 50ms).
29471      * @return {Roo.BasicDialog} this
29472      */
29473     anchorTo : function(el, alignment, offsets, monitorScroll){
29474         var action = function(){
29475             this.alignTo(el, alignment, offsets);
29476         };
29477         Roo.EventManager.onWindowResize(action, this);
29478         var tm = typeof monitorScroll;
29479         if(tm != 'undefined'){
29480             Roo.EventManager.on(window, 'scroll', action, this,
29481                 {buffer: tm == 'number' ? monitorScroll : 50});
29482         }
29483         action.call(this);
29484         return this;
29485     },
29486
29487     /**
29488      * Returns true if the dialog is visible
29489      * @return {Boolean}
29490      */
29491     isVisible : function(){
29492         return this.el.isVisible();
29493     },
29494
29495     // private
29496     animHide : function(callback){
29497         var b = Roo.get(this.animateTarget).getBox();
29498         this.proxy.show();
29499         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29500         this.el.hide();
29501         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29502                     this.hideEl.createDelegate(this, [callback]));
29503     },
29504
29505     /**
29506      * Hides the dialog.
29507      * @param {Function} callback (optional) Function to call when the dialog is hidden
29508      * @return {Roo.BasicDialog} this
29509      */
29510     hide : function(callback){
29511         if (this.fireEvent("beforehide", this) === false){
29512             return;
29513         }
29514         if(this.shadow){
29515             this.shadow.hide();
29516         }
29517         if(this.shim) {
29518           this.shim.hide();
29519         }
29520         // sometimes animateTarget seems to get set.. causing problems...
29521         // this just double checks..
29522         if(this.animateTarget && Roo.get(this.animateTarget)) {
29523            this.animHide(callback);
29524         }else{
29525             this.el.hide();
29526             this.hideEl(callback);
29527         }
29528         return this;
29529     },
29530
29531     // private
29532     hideEl : function(callback){
29533         this.proxy.hide();
29534         if(this.modal){
29535             this.mask.hide();
29536             Roo.get(document.body).removeClass("x-body-masked");
29537         }
29538         this.fireEvent("hide", this);
29539         if(typeof callback == "function"){
29540             callback();
29541         }
29542     },
29543
29544     // private
29545     hideAction : function(){
29546         this.setLeft("-10000px");
29547         this.setTop("-10000px");
29548         this.setStyle("visibility", "hidden");
29549     },
29550
29551     // private
29552     refreshSize : function(){
29553         this.size = this.el.getSize();
29554         this.xy = this.el.getXY();
29555         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29556     },
29557
29558     // private
29559     // z-index is managed by the DialogManager and may be overwritten at any time
29560     setZIndex : function(index){
29561         if(this.modal){
29562             this.mask.setStyle("z-index", index);
29563         }
29564         if(this.shim){
29565             this.shim.setStyle("z-index", ++index);
29566         }
29567         if(this.shadow){
29568             this.shadow.setZIndex(++index);
29569         }
29570         this.el.setStyle("z-index", ++index);
29571         if(this.proxy){
29572             this.proxy.setStyle("z-index", ++index);
29573         }
29574         if(this.resizer){
29575             this.resizer.proxy.setStyle("z-index", ++index);
29576         }
29577
29578         this.lastZIndex = index;
29579     },
29580
29581     /**
29582      * Returns the element for this dialog
29583      * @return {Roo.Element} The underlying dialog Element
29584      */
29585     getEl : function(){
29586         return this.el;
29587     }
29588 });
29589
29590 /**
29591  * @class Roo.DialogManager
29592  * Provides global access to BasicDialogs that have been created and
29593  * support for z-indexing (layering) multiple open dialogs.
29594  */
29595 Roo.DialogManager = function(){
29596     var list = {};
29597     var accessList = [];
29598     var front = null;
29599
29600     // private
29601     var sortDialogs = function(d1, d2){
29602         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29603     };
29604
29605     // private
29606     var orderDialogs = function(){
29607         accessList.sort(sortDialogs);
29608         var seed = Roo.DialogManager.zseed;
29609         for(var i = 0, len = accessList.length; i < len; i++){
29610             var dlg = accessList[i];
29611             if(dlg){
29612                 dlg.setZIndex(seed + (i*10));
29613             }
29614         }
29615     };
29616
29617     return {
29618         /**
29619          * The starting z-index for BasicDialogs (defaults to 9000)
29620          * @type Number The z-index value
29621          */
29622         zseed : 9000,
29623
29624         // private
29625         register : function(dlg){
29626             list[dlg.id] = dlg;
29627             accessList.push(dlg);
29628         },
29629
29630         // private
29631         unregister : function(dlg){
29632             delete list[dlg.id];
29633             var i=0;
29634             var len=0;
29635             if(!accessList.indexOf){
29636                 for(  i = 0, len = accessList.length; i < len; i++){
29637                     if(accessList[i] == dlg){
29638                         accessList.splice(i, 1);
29639                         return;
29640                     }
29641                 }
29642             }else{
29643                  i = accessList.indexOf(dlg);
29644                 if(i != -1){
29645                     accessList.splice(i, 1);
29646                 }
29647             }
29648         },
29649
29650         /**
29651          * Gets a registered dialog by id
29652          * @param {String/Object} id The id of the dialog or a dialog
29653          * @return {Roo.BasicDialog} this
29654          */
29655         get : function(id){
29656             return typeof id == "object" ? id : list[id];
29657         },
29658
29659         /**
29660          * Brings the specified dialog to the front
29661          * @param {String/Object} dlg The id of the dialog or a dialog
29662          * @return {Roo.BasicDialog} this
29663          */
29664         bringToFront : function(dlg){
29665             dlg = this.get(dlg);
29666             if(dlg != front){
29667                 front = dlg;
29668                 dlg._lastAccess = new Date().getTime();
29669                 orderDialogs();
29670             }
29671             return dlg;
29672         },
29673
29674         /**
29675          * Sends the specified dialog to the back
29676          * @param {String/Object} dlg The id of the dialog or a dialog
29677          * @return {Roo.BasicDialog} this
29678          */
29679         sendToBack : function(dlg){
29680             dlg = this.get(dlg);
29681             dlg._lastAccess = -(new Date().getTime());
29682             orderDialogs();
29683             return dlg;
29684         },
29685
29686         /**
29687          * Hides all dialogs
29688          */
29689         hideAll : function(){
29690             for(var id in list){
29691                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29692                     list[id].hide();
29693                 }
29694             }
29695         }
29696     };
29697 }();
29698
29699 /**
29700  * @class Roo.LayoutDialog
29701  * @extends Roo.BasicDialog
29702  * Dialog which provides adjustments for working with a layout in a Dialog.
29703  * Add your necessary layout config options to the dialog's config.<br>
29704  * Example usage (including a nested layout):
29705  * <pre><code>
29706 if(!dialog){
29707     dialog = new Roo.LayoutDialog("download-dlg", {
29708         modal: true,
29709         width:600,
29710         height:450,
29711         shadow:true,
29712         minWidth:500,
29713         minHeight:350,
29714         autoTabs:true,
29715         proxyDrag:true,
29716         // layout config merges with the dialog config
29717         center:{
29718             tabPosition: "top",
29719             alwaysShowTabs: true
29720         }
29721     });
29722     dialog.addKeyListener(27, dialog.hide, dialog);
29723     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29724     dialog.addButton("Build It!", this.getDownload, this);
29725
29726     // we can even add nested layouts
29727     var innerLayout = new Roo.BorderLayout("dl-inner", {
29728         east: {
29729             initialSize: 200,
29730             autoScroll:true,
29731             split:true
29732         },
29733         center: {
29734             autoScroll:true
29735         }
29736     });
29737     innerLayout.beginUpdate();
29738     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29739     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29740     innerLayout.endUpdate(true);
29741
29742     var layout = dialog.getLayout();
29743     layout.beginUpdate();
29744     layout.add("center", new Roo.ContentPanel("standard-panel",
29745                         {title: "Download the Source", fitToFrame:true}));
29746     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29747                {title: "Build your own roo.js"}));
29748     layout.getRegion("center").showPanel(sp);
29749     layout.endUpdate();
29750 }
29751 </code></pre>
29752     * @constructor
29753     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29754     * @param {Object} config configuration options
29755   */
29756 Roo.LayoutDialog = function(el, cfg){
29757     
29758     var config=  cfg;
29759     if (typeof(cfg) == 'undefined') {
29760         config = Roo.apply({}, el);
29761         // not sure why we use documentElement here.. - it should always be body.
29762         // IE7 borks horribly if we use documentElement.
29763         // webkit also does not like documentElement - it creates a body element...
29764         el = Roo.get( document.body || document.documentElement ).createChild();
29765         //config.autoCreate = true;
29766     }
29767     
29768     
29769     config.autoTabs = false;
29770     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29771     this.body.setStyle({overflow:"hidden", position:"relative"});
29772     this.layout = new Roo.BorderLayout(this.body.dom, config);
29773     this.layout.monitorWindowResize = false;
29774     this.el.addClass("x-dlg-auto-layout");
29775     // fix case when center region overwrites center function
29776     this.center = Roo.BasicDialog.prototype.center;
29777     this.on("show", this.layout.layout, this.layout, true);
29778     if (config.items) {
29779         var xitems = config.items;
29780         delete config.items;
29781         Roo.each(xitems, this.addxtype, this);
29782     }
29783     
29784     
29785 };
29786 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29787     /**
29788      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29789      * @deprecated
29790      */
29791     endUpdate : function(){
29792         this.layout.endUpdate();
29793     },
29794
29795     /**
29796      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29797      *  @deprecated
29798      */
29799     beginUpdate : function(){
29800         this.layout.beginUpdate();
29801     },
29802
29803     /**
29804      * Get the BorderLayout for this dialog
29805      * @return {Roo.BorderLayout}
29806      */
29807     getLayout : function(){
29808         return this.layout;
29809     },
29810
29811     showEl : function(){
29812         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29813         if(Roo.isIE7){
29814             this.layout.layout();
29815         }
29816     },
29817
29818     // private
29819     // Use the syncHeightBeforeShow config option to control this automatically
29820     syncBodyHeight : function(){
29821         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29822         if(this.layout){this.layout.layout();}
29823     },
29824     
29825       /**
29826      * Add an xtype element (actually adds to the layout.)
29827      * @return {Object} xdata xtype object data.
29828      */
29829     
29830     addxtype : function(c) {
29831         return this.layout.addxtype(c);
29832     }
29833 });/*
29834  * Based on:
29835  * Ext JS Library 1.1.1
29836  * Copyright(c) 2006-2007, Ext JS, LLC.
29837  *
29838  * Originally Released Under LGPL - original licence link has changed is not relivant.
29839  *
29840  * Fork - LGPL
29841  * <script type="text/javascript">
29842  */
29843  
29844 /**
29845  * @class Roo.MessageBox
29846  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29847  * Example usage:
29848  *<pre><code>
29849 // Basic alert:
29850 Roo.Msg.alert('Status', 'Changes saved successfully.');
29851
29852 // Prompt for user data:
29853 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29854     if (btn == 'ok'){
29855         // process text value...
29856     }
29857 });
29858
29859 // Show a dialog using config options:
29860 Roo.Msg.show({
29861    title:'Save Changes?',
29862    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29863    buttons: Roo.Msg.YESNOCANCEL,
29864    fn: processResult,
29865    animEl: 'elId'
29866 });
29867 </code></pre>
29868  * @singleton
29869  */
29870 Roo.MessageBox = function(){
29871     var dlg, opt, mask, waitTimer;
29872     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29873     var buttons, activeTextEl, bwidth;
29874
29875     // private
29876     var handleButton = function(button){
29877         dlg.hide();
29878         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29879     };
29880
29881     // private
29882     var handleHide = function(){
29883         if(opt && opt.cls){
29884             dlg.el.removeClass(opt.cls);
29885         }
29886         if(waitTimer){
29887             Roo.TaskMgr.stop(waitTimer);
29888             waitTimer = null;
29889         }
29890     };
29891
29892     // private
29893     var updateButtons = function(b){
29894         var width = 0;
29895         if(!b){
29896             buttons["ok"].hide();
29897             buttons["cancel"].hide();
29898             buttons["yes"].hide();
29899             buttons["no"].hide();
29900             dlg.footer.dom.style.display = 'none';
29901             return width;
29902         }
29903         dlg.footer.dom.style.display = '';
29904         for(var k in buttons){
29905             if(typeof buttons[k] != "function"){
29906                 if(b[k]){
29907                     buttons[k].show();
29908                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29909                     width += buttons[k].el.getWidth()+15;
29910                 }else{
29911                     buttons[k].hide();
29912                 }
29913             }
29914         }
29915         return width;
29916     };
29917
29918     // private
29919     var handleEsc = function(d, k, e){
29920         if(opt && opt.closable !== false){
29921             dlg.hide();
29922         }
29923         if(e){
29924             e.stopEvent();
29925         }
29926     };
29927
29928     return {
29929         /**
29930          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29931          * @return {Roo.BasicDialog} The BasicDialog element
29932          */
29933         getDialog : function(){
29934            if(!dlg){
29935                 dlg = new Roo.BasicDialog("x-msg-box", {
29936                     autoCreate : true,
29937                     shadow: true,
29938                     draggable: true,
29939                     resizable:false,
29940                     constraintoviewport:false,
29941                     fixedcenter:true,
29942                     collapsible : false,
29943                     shim:true,
29944                     modal: true,
29945                     width:400, height:100,
29946                     buttonAlign:"center",
29947                     closeClick : function(){
29948                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29949                             handleButton("no");
29950                         }else{
29951                             handleButton("cancel");
29952                         }
29953                     }
29954                 });
29955                 dlg.on("hide", handleHide);
29956                 mask = dlg.mask;
29957                 dlg.addKeyListener(27, handleEsc);
29958                 buttons = {};
29959                 var bt = this.buttonText;
29960                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29961                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29962                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29963                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29964                 bodyEl = dlg.body.createChild({
29965
29966                     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>'
29967                 });
29968                 msgEl = bodyEl.dom.firstChild;
29969                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29970                 textboxEl.enableDisplayMode();
29971                 textboxEl.addKeyListener([10,13], function(){
29972                     if(dlg.isVisible() && opt && opt.buttons){
29973                         if(opt.buttons.ok){
29974                             handleButton("ok");
29975                         }else if(opt.buttons.yes){
29976                             handleButton("yes");
29977                         }
29978                     }
29979                 });
29980                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29981                 textareaEl.enableDisplayMode();
29982                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29983                 progressEl.enableDisplayMode();
29984                 var pf = progressEl.dom.firstChild;
29985                 if (pf) {
29986                     pp = Roo.get(pf.firstChild);
29987                     pp.setHeight(pf.offsetHeight);
29988                 }
29989                 
29990             }
29991             return dlg;
29992         },
29993
29994         /**
29995          * Updates the message box body text
29996          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29997          * the XHTML-compliant non-breaking space character '&amp;#160;')
29998          * @return {Roo.MessageBox} This message box
29999          */
30000         updateText : function(text){
30001             if(!dlg.isVisible() && !opt.width){
30002                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30003             }
30004             msgEl.innerHTML = text || '&#160;';
30005             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30006                         Math.max(opt.minWidth || this.minWidth, bwidth));
30007             if(opt.prompt){
30008                 activeTextEl.setWidth(w);
30009             }
30010             if(dlg.isVisible()){
30011                 dlg.fixedcenter = false;
30012             }
30013             dlg.setContentSize(w, bodyEl.getHeight());
30014             if(dlg.isVisible()){
30015                 dlg.fixedcenter = true;
30016             }
30017             return this;
30018         },
30019
30020         /**
30021          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30022          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30023          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30024          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30025          * @return {Roo.MessageBox} This message box
30026          */
30027         updateProgress : function(value, text){
30028             if(text){
30029                 this.updateText(text);
30030             }
30031             if (pp) { // weird bug on my firefox - for some reason this is not defined
30032                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30033             }
30034             return this;
30035         },        
30036
30037         /**
30038          * Returns true if the message box is currently displayed
30039          * @return {Boolean} True if the message box is visible, else false
30040          */
30041         isVisible : function(){
30042             return dlg && dlg.isVisible();  
30043         },
30044
30045         /**
30046          * Hides the message box if it is displayed
30047          */
30048         hide : function(){
30049             if(this.isVisible()){
30050                 dlg.hide();
30051             }  
30052         },
30053
30054         /**
30055          * Displays a new message box, or reinitializes an existing message box, based on the config options
30056          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30057          * The following config object properties are supported:
30058          * <pre>
30059 Property    Type             Description
30060 ----------  ---------------  ------------------------------------------------------------------------------------
30061 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30062                                    closes (defaults to undefined)
30063 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30064                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30065 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30066                                    progress and wait dialogs will ignore this property and always hide the
30067                                    close button as they can only be closed programmatically.
30068 cls               String           A custom CSS class to apply to the message box element
30069 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30070                                    displayed (defaults to 75)
30071 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30072                                    function will be btn (the name of the button that was clicked, if applicable,
30073                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30074                                    Progress and wait dialogs will ignore this option since they do not respond to
30075                                    user actions and can only be closed programmatically, so any required function
30076                                    should be called by the same code after it closes the dialog.
30077 icon              String           A CSS class that provides a background image to be used as an icon for
30078                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30079 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30080 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30081 modal             Boolean          False to allow user interaction with the page while the message box is
30082                                    displayed (defaults to true)
30083 msg               String           A string that will replace the existing message box body text (defaults
30084                                    to the XHTML-compliant non-breaking space character '&#160;')
30085 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30086 progress          Boolean          True to display a progress bar (defaults to false)
30087 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30088 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30089 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30090 title             String           The title text
30091 value             String           The string value to set into the active textbox element if displayed
30092 wait              Boolean          True to display a progress bar (defaults to false)
30093 width             Number           The width of the dialog in pixels
30094 </pre>
30095          *
30096          * Example usage:
30097          * <pre><code>
30098 Roo.Msg.show({
30099    title: 'Address',
30100    msg: 'Please enter your address:',
30101    width: 300,
30102    buttons: Roo.MessageBox.OKCANCEL,
30103    multiline: true,
30104    fn: saveAddress,
30105    animEl: 'addAddressBtn'
30106 });
30107 </code></pre>
30108          * @param {Object} config Configuration options
30109          * @return {Roo.MessageBox} This message box
30110          */
30111         show : function(options){
30112             if(this.isVisible()){
30113                 this.hide();
30114             }
30115             var d = this.getDialog();
30116             opt = options;
30117             d.setTitle(opt.title || "&#160;");
30118             d.close.setDisplayed(opt.closable !== false);
30119             activeTextEl = textboxEl;
30120             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30121             if(opt.prompt){
30122                 if(opt.multiline){
30123                     textboxEl.hide();
30124                     textareaEl.show();
30125                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30126                         opt.multiline : this.defaultTextHeight);
30127                     activeTextEl = textareaEl;
30128                 }else{
30129                     textboxEl.show();
30130                     textareaEl.hide();
30131                 }
30132             }else{
30133                 textboxEl.hide();
30134                 textareaEl.hide();
30135             }
30136             progressEl.setDisplayed(opt.progress === true);
30137             this.updateProgress(0);
30138             activeTextEl.dom.value = opt.value || "";
30139             if(opt.prompt){
30140                 dlg.setDefaultButton(activeTextEl);
30141             }else{
30142                 var bs = opt.buttons;
30143                 var db = null;
30144                 if(bs && bs.ok){
30145                     db = buttons["ok"];
30146                 }else if(bs && bs.yes){
30147                     db = buttons["yes"];
30148                 }
30149                 dlg.setDefaultButton(db);
30150             }
30151             bwidth = updateButtons(opt.buttons);
30152             this.updateText(opt.msg);
30153             if(opt.cls){
30154                 d.el.addClass(opt.cls);
30155             }
30156             d.proxyDrag = opt.proxyDrag === true;
30157             d.modal = opt.modal !== false;
30158             d.mask = opt.modal !== false ? mask : false;
30159             if(!d.isVisible()){
30160                 // force it to the end of the z-index stack so it gets a cursor in FF
30161                 document.body.appendChild(dlg.el.dom);
30162                 d.animateTarget = null;
30163                 d.show(options.animEl);
30164             }
30165             return this;
30166         },
30167
30168         /**
30169          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30170          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30171          * and closing the message box when the process is complete.
30172          * @param {String} title The title bar text
30173          * @param {String} msg The message box body text
30174          * @return {Roo.MessageBox} This message box
30175          */
30176         progress : function(title, msg){
30177             this.show({
30178                 title : title,
30179                 msg : msg,
30180                 buttons: false,
30181                 progress:true,
30182                 closable:false,
30183                 minWidth: this.minProgressWidth,
30184                 modal : true
30185             });
30186             return this;
30187         },
30188
30189         /**
30190          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30191          * If a callback function is passed it will be called after the user clicks the button, and the
30192          * id of the button that was clicked will be passed as the only parameter to the callback
30193          * (could also be the top-right close button).
30194          * @param {String} title The title bar text
30195          * @param {String} msg The message box body text
30196          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30197          * @param {Object} scope (optional) The scope of the callback function
30198          * @return {Roo.MessageBox} This message box
30199          */
30200         alert : function(title, msg, fn, scope){
30201             this.show({
30202                 title : title,
30203                 msg : msg,
30204                 buttons: this.OK,
30205                 fn: fn,
30206                 scope : scope,
30207                 modal : true
30208             });
30209             return this;
30210         },
30211
30212         /**
30213          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30214          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30215          * You are responsible for closing the message box when the process is complete.
30216          * @param {String} msg The message box body text
30217          * @param {String} title (optional) The title bar text
30218          * @return {Roo.MessageBox} This message box
30219          */
30220         wait : function(msg, title){
30221             this.show({
30222                 title : title,
30223                 msg : msg,
30224                 buttons: false,
30225                 closable:false,
30226                 progress:true,
30227                 modal:true,
30228                 width:300,
30229                 wait:true
30230             });
30231             waitTimer = Roo.TaskMgr.start({
30232                 run: function(i){
30233                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30234                 },
30235                 interval: 1000
30236             });
30237             return this;
30238         },
30239
30240         /**
30241          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30242          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30243          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30244          * @param {String} title The title bar text
30245          * @param {String} msg The message box body text
30246          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30247          * @param {Object} scope (optional) The scope of the callback function
30248          * @return {Roo.MessageBox} This message box
30249          */
30250         confirm : function(title, msg, fn, scope){
30251             this.show({
30252                 title : title,
30253                 msg : msg,
30254                 buttons: this.YESNO,
30255                 fn: fn,
30256                 scope : scope,
30257                 modal : true
30258             });
30259             return this;
30260         },
30261
30262         /**
30263          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30264          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30265          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30266          * (could also be the top-right close button) and the text that was entered will be passed as the two
30267          * parameters to the callback.
30268          * @param {String} title The title bar text
30269          * @param {String} msg The message box body text
30270          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30271          * @param {Object} scope (optional) The scope of the callback function
30272          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30273          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30274          * @return {Roo.MessageBox} This message box
30275          */
30276         prompt : function(title, msg, fn, scope, multiline){
30277             this.show({
30278                 title : title,
30279                 msg : msg,
30280                 buttons: this.OKCANCEL,
30281                 fn: fn,
30282                 minWidth:250,
30283                 scope : scope,
30284                 prompt:true,
30285                 multiline: multiline,
30286                 modal : true
30287             });
30288             return this;
30289         },
30290
30291         /**
30292          * Button config that displays a single OK button
30293          * @type Object
30294          */
30295         OK : {ok:true},
30296         /**
30297          * Button config that displays Yes and No buttons
30298          * @type Object
30299          */
30300         YESNO : {yes:true, no:true},
30301         /**
30302          * Button config that displays OK and Cancel buttons
30303          * @type Object
30304          */
30305         OKCANCEL : {ok:true, cancel:true},
30306         /**
30307          * Button config that displays Yes, No and Cancel buttons
30308          * @type Object
30309          */
30310         YESNOCANCEL : {yes:true, no:true, cancel:true},
30311
30312         /**
30313          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30314          * @type Number
30315          */
30316         defaultTextHeight : 75,
30317         /**
30318          * The maximum width in pixels of the message box (defaults to 600)
30319          * @type Number
30320          */
30321         maxWidth : 600,
30322         /**
30323          * The minimum width in pixels of the message box (defaults to 100)
30324          * @type Number
30325          */
30326         minWidth : 100,
30327         /**
30328          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30329          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30330          * @type Number
30331          */
30332         minProgressWidth : 250,
30333         /**
30334          * An object containing the default button text strings that can be overriden for localized language support.
30335          * Supported properties are: ok, cancel, yes and no.
30336          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30337          * @type Object
30338          */
30339         buttonText : {
30340             ok : "OK",
30341             cancel : "Cancel",
30342             yes : "Yes",
30343             no : "No"
30344         }
30345     };
30346 }();
30347
30348 /**
30349  * Shorthand for {@link Roo.MessageBox}
30350  */
30351 Roo.Msg = Roo.MessageBox;/*
30352  * Based on:
30353  * Ext JS Library 1.1.1
30354  * Copyright(c) 2006-2007, Ext JS, LLC.
30355  *
30356  * Originally Released Under LGPL - original licence link has changed is not relivant.
30357  *
30358  * Fork - LGPL
30359  * <script type="text/javascript">
30360  */
30361 /**
30362  * @class Roo.QuickTips
30363  * Provides attractive and customizable tooltips for any element.
30364  * @singleton
30365  */
30366 Roo.QuickTips = function(){
30367     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30368     var ce, bd, xy, dd;
30369     var visible = false, disabled = true, inited = false;
30370     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30371     
30372     var onOver = function(e){
30373         if(disabled){
30374             return;
30375         }
30376         var t = e.getTarget();
30377         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30378             return;
30379         }
30380         if(ce && t == ce.el){
30381             clearTimeout(hideProc);
30382             return;
30383         }
30384         if(t && tagEls[t.id]){
30385             tagEls[t.id].el = t;
30386             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30387             return;
30388         }
30389         var ttp, et = Roo.fly(t);
30390         var ns = cfg.namespace;
30391         if(tm.interceptTitles && t.title){
30392             ttp = t.title;
30393             t.qtip = ttp;
30394             t.removeAttribute("title");
30395             e.preventDefault();
30396         }else{
30397             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30398         }
30399         if(ttp){
30400             showProc = show.defer(tm.showDelay, tm, [{
30401                 el: t, 
30402                 text: ttp, 
30403                 width: et.getAttributeNS(ns, cfg.width),
30404                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30405                 title: et.getAttributeNS(ns, cfg.title),
30406                     cls: et.getAttributeNS(ns, cfg.cls)
30407             }]);
30408         }
30409     };
30410     
30411     var onOut = function(e){
30412         clearTimeout(showProc);
30413         var t = e.getTarget();
30414         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30415             hideProc = setTimeout(hide, tm.hideDelay);
30416         }
30417     };
30418     
30419     var onMove = function(e){
30420         if(disabled){
30421             return;
30422         }
30423         xy = e.getXY();
30424         xy[1] += 18;
30425         if(tm.trackMouse && ce){
30426             el.setXY(xy);
30427         }
30428     };
30429     
30430     var onDown = function(e){
30431         clearTimeout(showProc);
30432         clearTimeout(hideProc);
30433         if(!e.within(el)){
30434             if(tm.hideOnClick){
30435                 hide();
30436                 tm.disable();
30437                 tm.enable.defer(100, tm);
30438             }
30439         }
30440     };
30441     
30442     var getPad = function(){
30443         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30444     };
30445
30446     var show = function(o){
30447         if(disabled){
30448             return;
30449         }
30450         clearTimeout(dismissProc);
30451         ce = o;
30452         if(removeCls){ // in case manually hidden
30453             el.removeClass(removeCls);
30454             removeCls = null;
30455         }
30456         if(ce.cls){
30457             el.addClass(ce.cls);
30458             removeCls = ce.cls;
30459         }
30460         if(ce.title){
30461             tipTitle.update(ce.title);
30462             tipTitle.show();
30463         }else{
30464             tipTitle.update('');
30465             tipTitle.hide();
30466         }
30467         el.dom.style.width  = tm.maxWidth+'px';
30468         //tipBody.dom.style.width = '';
30469         tipBodyText.update(o.text);
30470         var p = getPad(), w = ce.width;
30471         if(!w){
30472             var td = tipBodyText.dom;
30473             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30474             if(aw > tm.maxWidth){
30475                 w = tm.maxWidth;
30476             }else if(aw < tm.minWidth){
30477                 w = tm.minWidth;
30478             }else{
30479                 w = aw;
30480             }
30481         }
30482         //tipBody.setWidth(w);
30483         el.setWidth(parseInt(w, 10) + p);
30484         if(ce.autoHide === false){
30485             close.setDisplayed(true);
30486             if(dd){
30487                 dd.unlock();
30488             }
30489         }else{
30490             close.setDisplayed(false);
30491             if(dd){
30492                 dd.lock();
30493             }
30494         }
30495         if(xy){
30496             el.avoidY = xy[1]-18;
30497             el.setXY(xy);
30498         }
30499         if(tm.animate){
30500             el.setOpacity(.1);
30501             el.setStyle("visibility", "visible");
30502             el.fadeIn({callback: afterShow});
30503         }else{
30504             afterShow();
30505         }
30506     };
30507     
30508     var afterShow = function(){
30509         if(ce){
30510             el.show();
30511             esc.enable();
30512             if(tm.autoDismiss && ce.autoHide !== false){
30513                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30514             }
30515         }
30516     };
30517     
30518     var hide = function(noanim){
30519         clearTimeout(dismissProc);
30520         clearTimeout(hideProc);
30521         ce = null;
30522         if(el.isVisible()){
30523             esc.disable();
30524             if(noanim !== true && tm.animate){
30525                 el.fadeOut({callback: afterHide});
30526             }else{
30527                 afterHide();
30528             } 
30529         }
30530     };
30531     
30532     var afterHide = function(){
30533         el.hide();
30534         if(removeCls){
30535             el.removeClass(removeCls);
30536             removeCls = null;
30537         }
30538     };
30539     
30540     return {
30541         /**
30542         * @cfg {Number} minWidth
30543         * The minimum width of the quick tip (defaults to 40)
30544         */
30545        minWidth : 40,
30546         /**
30547         * @cfg {Number} maxWidth
30548         * The maximum width of the quick tip (defaults to 300)
30549         */
30550        maxWidth : 300,
30551         /**
30552         * @cfg {Boolean} interceptTitles
30553         * True to automatically use the element's DOM title value if available (defaults to false)
30554         */
30555        interceptTitles : false,
30556         /**
30557         * @cfg {Boolean} trackMouse
30558         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30559         */
30560        trackMouse : false,
30561         /**
30562         * @cfg {Boolean} hideOnClick
30563         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30564         */
30565        hideOnClick : true,
30566         /**
30567         * @cfg {Number} showDelay
30568         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30569         */
30570        showDelay : 500,
30571         /**
30572         * @cfg {Number} hideDelay
30573         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30574         */
30575        hideDelay : 200,
30576         /**
30577         * @cfg {Boolean} autoHide
30578         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30579         * Used in conjunction with hideDelay.
30580         */
30581        autoHide : true,
30582         /**
30583         * @cfg {Boolean}
30584         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30585         * (defaults to true).  Used in conjunction with autoDismissDelay.
30586         */
30587        autoDismiss : true,
30588         /**
30589         * @cfg {Number}
30590         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30591         */
30592        autoDismissDelay : 5000,
30593        /**
30594         * @cfg {Boolean} animate
30595         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30596         */
30597        animate : false,
30598
30599        /**
30600         * @cfg {String} title
30601         * Title text to display (defaults to '').  This can be any valid HTML markup.
30602         */
30603         title: '',
30604        /**
30605         * @cfg {String} text
30606         * Body text to display (defaults to '').  This can be any valid HTML markup.
30607         */
30608         text : '',
30609        /**
30610         * @cfg {String} cls
30611         * A CSS class to apply to the base quick tip element (defaults to '').
30612         */
30613         cls : '',
30614        /**
30615         * @cfg {Number} width
30616         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30617         * minWidth or maxWidth.
30618         */
30619         width : null,
30620
30621     /**
30622      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30623      * or display QuickTips in a page.
30624      */
30625        init : function(){
30626           tm = Roo.QuickTips;
30627           cfg = tm.tagConfig;
30628           if(!inited){
30629               if(!Roo.isReady){ // allow calling of init() before onReady
30630                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30631                   return;
30632               }
30633               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30634               el.fxDefaults = {stopFx: true};
30635               // maximum custom styling
30636               //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>');
30637               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>');              
30638               tipTitle = el.child('h3');
30639               tipTitle.enableDisplayMode("block");
30640               tipBody = el.child('div.x-tip-bd');
30641               tipBodyText = el.child('div.x-tip-bd-inner');
30642               //bdLeft = el.child('div.x-tip-bd-left');
30643               //bdRight = el.child('div.x-tip-bd-right');
30644               close = el.child('div.x-tip-close');
30645               close.enableDisplayMode("block");
30646               close.on("click", hide);
30647               var d = Roo.get(document);
30648               d.on("mousedown", onDown);
30649               d.on("mouseover", onOver);
30650               d.on("mouseout", onOut);
30651               d.on("mousemove", onMove);
30652               esc = d.addKeyListener(27, hide);
30653               esc.disable();
30654               if(Roo.dd.DD){
30655                   dd = el.initDD("default", null, {
30656                       onDrag : function(){
30657                           el.sync();  
30658                       }
30659                   });
30660                   dd.setHandleElId(tipTitle.id);
30661                   dd.lock();
30662               }
30663               inited = true;
30664           }
30665           this.enable(); 
30666        },
30667
30668     /**
30669      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30670      * are supported:
30671      * <pre>
30672 Property    Type                   Description
30673 ----------  ---------------------  ------------------------------------------------------------------------
30674 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30675      * </ul>
30676      * @param {Object} config The config object
30677      */
30678        register : function(config){
30679            var cs = config instanceof Array ? config : arguments;
30680            for(var i = 0, len = cs.length; i < len; i++) {
30681                var c = cs[i];
30682                var target = c.target;
30683                if(target){
30684                    if(target instanceof Array){
30685                        for(var j = 0, jlen = target.length; j < jlen; j++){
30686                            tagEls[target[j]] = c;
30687                        }
30688                    }else{
30689                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30690                    }
30691                }
30692            }
30693        },
30694
30695     /**
30696      * Removes this quick tip from its element and destroys it.
30697      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30698      */
30699        unregister : function(el){
30700            delete tagEls[Roo.id(el)];
30701        },
30702
30703     /**
30704      * Enable this quick tip.
30705      */
30706        enable : function(){
30707            if(inited && disabled){
30708                locks.pop();
30709                if(locks.length < 1){
30710                    disabled = false;
30711                }
30712            }
30713        },
30714
30715     /**
30716      * Disable this quick tip.
30717      */
30718        disable : function(){
30719           disabled = true;
30720           clearTimeout(showProc);
30721           clearTimeout(hideProc);
30722           clearTimeout(dismissProc);
30723           if(ce){
30724               hide(true);
30725           }
30726           locks.push(1);
30727        },
30728
30729     /**
30730      * Returns true if the quick tip is enabled, else false.
30731      */
30732        isEnabled : function(){
30733             return !disabled;
30734        },
30735
30736         // private
30737        tagConfig : {
30738            namespace : "ext",
30739            attribute : "qtip",
30740            width : "width",
30741            target : "target",
30742            title : "qtitle",
30743            hide : "hide",
30744            cls : "qclass"
30745        }
30746    };
30747 }();
30748
30749 // backwards compat
30750 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30751  * Based on:
30752  * Ext JS Library 1.1.1
30753  * Copyright(c) 2006-2007, Ext JS, LLC.
30754  *
30755  * Originally Released Under LGPL - original licence link has changed is not relivant.
30756  *
30757  * Fork - LGPL
30758  * <script type="text/javascript">
30759  */
30760  
30761
30762 /**
30763  * @class Roo.tree.TreePanel
30764  * @extends Roo.data.Tree
30765
30766  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30767  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30768  * @cfg {Boolean} enableDD true to enable drag and drop
30769  * @cfg {Boolean} enableDrag true to enable just drag
30770  * @cfg {Boolean} enableDrop true to enable just drop
30771  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30772  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30773  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30774  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30775  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30776  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30777  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30778  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30779  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30780  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30781  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30782  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30783  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30784  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30785  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30786  * 
30787  * @constructor
30788  * @param {String/HTMLElement/Element} el The container element
30789  * @param {Object} config
30790  */
30791 Roo.tree.TreePanel = function(el, config){
30792     var root = false;
30793     var loader = false;
30794     if (config.root) {
30795         root = config.root;
30796         delete config.root;
30797     }
30798     if (config.loader) {
30799         loader = config.loader;
30800         delete config.loader;
30801     }
30802     
30803     Roo.apply(this, config);
30804     Roo.tree.TreePanel.superclass.constructor.call(this);
30805     this.el = Roo.get(el);
30806     this.el.addClass('x-tree');
30807     //console.log(root);
30808     if (root) {
30809         this.setRootNode( Roo.factory(root, Roo.tree));
30810     }
30811     if (loader) {
30812         this.loader = Roo.factory(loader, Roo.tree);
30813     }
30814    /**
30815     * Read-only. The id of the container element becomes this TreePanel's id.
30816     */
30817    this.id = this.el.id;
30818    this.addEvents({
30819         /**
30820         * @event beforeload
30821         * Fires before a node is loaded, return false to cancel
30822         * @param {Node} node The node being loaded
30823         */
30824         "beforeload" : true,
30825         /**
30826         * @event load
30827         * Fires when a node is loaded
30828         * @param {Node} node The node that was loaded
30829         */
30830         "load" : true,
30831         /**
30832         * @event textchange
30833         * Fires when the text for a node is changed
30834         * @param {Node} node The node
30835         * @param {String} text The new text
30836         * @param {String} oldText The old text
30837         */
30838         "textchange" : true,
30839         /**
30840         * @event beforeexpand
30841         * Fires before a node is expanded, return false to cancel.
30842         * @param {Node} node The node
30843         * @param {Boolean} deep
30844         * @param {Boolean} anim
30845         */
30846         "beforeexpand" : true,
30847         /**
30848         * @event beforecollapse
30849         * Fires before a node is collapsed, return false to cancel.
30850         * @param {Node} node The node
30851         * @param {Boolean} deep
30852         * @param {Boolean} anim
30853         */
30854         "beforecollapse" : true,
30855         /**
30856         * @event expand
30857         * Fires when a node is expanded
30858         * @param {Node} node The node
30859         */
30860         "expand" : true,
30861         /**
30862         * @event disabledchange
30863         * Fires when the disabled status of a node changes
30864         * @param {Node} node The node
30865         * @param {Boolean} disabled
30866         */
30867         "disabledchange" : true,
30868         /**
30869         * @event collapse
30870         * Fires when a node is collapsed
30871         * @param {Node} node The node
30872         */
30873         "collapse" : true,
30874         /**
30875         * @event beforeclick
30876         * Fires before click processing on a node. Return false to cancel the default action.
30877         * @param {Node} node The node
30878         * @param {Roo.EventObject} e The event object
30879         */
30880         "beforeclick":true,
30881         /**
30882         * @event checkchange
30883         * Fires when a node with a checkbox's checked property changes
30884         * @param {Node} this This node
30885         * @param {Boolean} checked
30886         */
30887         "checkchange":true,
30888         /**
30889         * @event click
30890         * Fires when a node is clicked
30891         * @param {Node} node The node
30892         * @param {Roo.EventObject} e The event object
30893         */
30894         "click":true,
30895         /**
30896         * @event dblclick
30897         * Fires when a node is double clicked
30898         * @param {Node} node The node
30899         * @param {Roo.EventObject} e The event object
30900         */
30901         "dblclick":true,
30902         /**
30903         * @event contextmenu
30904         * Fires when a node is right clicked
30905         * @param {Node} node The node
30906         * @param {Roo.EventObject} e The event object
30907         */
30908         "contextmenu":true,
30909         /**
30910         * @event beforechildrenrendered
30911         * Fires right before the child nodes for a node are rendered
30912         * @param {Node} node The node
30913         */
30914         "beforechildrenrendered":true,
30915        /**
30916              * @event startdrag
30917              * Fires when a node starts being dragged
30918              * @param {Roo.tree.TreePanel} this
30919              * @param {Roo.tree.TreeNode} node
30920              * @param {event} e The raw browser event
30921              */ 
30922             "startdrag" : true,
30923             /**
30924              * @event enddrag
30925              * Fires when a drag operation is complete
30926              * @param {Roo.tree.TreePanel} this
30927              * @param {Roo.tree.TreeNode} node
30928              * @param {event} e The raw browser event
30929              */
30930             "enddrag" : true,
30931             /**
30932              * @event dragdrop
30933              * Fires when a dragged node is dropped on a valid DD target
30934              * @param {Roo.tree.TreePanel} this
30935              * @param {Roo.tree.TreeNode} node
30936              * @param {DD} dd The dd it was dropped on
30937              * @param {event} e The raw browser event
30938              */
30939             "dragdrop" : true,
30940             /**
30941              * @event beforenodedrop
30942              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30943              * passed to handlers has the following properties:<br />
30944              * <ul style="padding:5px;padding-left:16px;">
30945              * <li>tree - The TreePanel</li>
30946              * <li>target - The node being targeted for the drop</li>
30947              * <li>data - The drag data from the drag source</li>
30948              * <li>point - The point of the drop - append, above or below</li>
30949              * <li>source - The drag source</li>
30950              * <li>rawEvent - Raw mouse event</li>
30951              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30952              * to be inserted by setting them on this object.</li>
30953              * <li>cancel - Set this to true to cancel the drop.</li>
30954              * </ul>
30955              * @param {Object} dropEvent
30956              */
30957             "beforenodedrop" : true,
30958             /**
30959              * @event nodedrop
30960              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30961              * passed to handlers has the following properties:<br />
30962              * <ul style="padding:5px;padding-left:16px;">
30963              * <li>tree - The TreePanel</li>
30964              * <li>target - The node being targeted for the drop</li>
30965              * <li>data - The drag data from the drag source</li>
30966              * <li>point - The point of the drop - append, above or below</li>
30967              * <li>source - The drag source</li>
30968              * <li>rawEvent - Raw mouse event</li>
30969              * <li>dropNode - Dropped node(s).</li>
30970              * </ul>
30971              * @param {Object} dropEvent
30972              */
30973             "nodedrop" : true,
30974              /**
30975              * @event nodedragover
30976              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30977              * passed to handlers has the following properties:<br />
30978              * <ul style="padding:5px;padding-left:16px;">
30979              * <li>tree - The TreePanel</li>
30980              * <li>target - The node being targeted for the drop</li>
30981              * <li>data - The drag data from the drag source</li>
30982              * <li>point - The point of the drop - append, above or below</li>
30983              * <li>source - The drag source</li>
30984              * <li>rawEvent - Raw mouse event</li>
30985              * <li>dropNode - Drop node(s) provided by the source.</li>
30986              * <li>cancel - Set this to true to signal drop not allowed.</li>
30987              * </ul>
30988              * @param {Object} dragOverEvent
30989              */
30990             "nodedragover" : true
30991         
30992    });
30993    if(this.singleExpand){
30994        this.on("beforeexpand", this.restrictExpand, this);
30995    }
30996 };
30997 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30998     rootVisible : true,
30999     animate: Roo.enableFx,
31000     lines : true,
31001     enableDD : false,
31002     hlDrop : Roo.enableFx,
31003   
31004     renderer: false,
31005     
31006     rendererTip: false,
31007     // private
31008     restrictExpand : function(node){
31009         var p = node.parentNode;
31010         if(p){
31011             if(p.expandedChild && p.expandedChild.parentNode == p){
31012                 p.expandedChild.collapse();
31013             }
31014             p.expandedChild = node;
31015         }
31016     },
31017
31018     // private override
31019     setRootNode : function(node){
31020         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31021         if(!this.rootVisible){
31022             node.ui = new Roo.tree.RootTreeNodeUI(node);
31023         }
31024         return node;
31025     },
31026
31027     /**
31028      * Returns the container element for this TreePanel
31029      */
31030     getEl : function(){
31031         return this.el;
31032     },
31033
31034     /**
31035      * Returns the default TreeLoader for this TreePanel
31036      */
31037     getLoader : function(){
31038         return this.loader;
31039     },
31040
31041     /**
31042      * Expand all nodes
31043      */
31044     expandAll : function(){
31045         this.root.expand(true);
31046     },
31047
31048     /**
31049      * Collapse all nodes
31050      */
31051     collapseAll : function(){
31052         this.root.collapse(true);
31053     },
31054
31055     /**
31056      * Returns the selection model used by this TreePanel
31057      */
31058     getSelectionModel : function(){
31059         if(!this.selModel){
31060             this.selModel = new Roo.tree.DefaultSelectionModel();
31061         }
31062         return this.selModel;
31063     },
31064
31065     /**
31066      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31067      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31068      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31069      * @return {Array}
31070      */
31071     getChecked : function(a, startNode){
31072         startNode = startNode || this.root;
31073         var r = [];
31074         var f = function(){
31075             if(this.attributes.checked){
31076                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31077             }
31078         }
31079         startNode.cascade(f);
31080         return r;
31081     },
31082
31083     /**
31084      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31085      * @param {String} path
31086      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31087      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31088      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31089      */
31090     expandPath : function(path, attr, callback){
31091         attr = attr || "id";
31092         var keys = path.split(this.pathSeparator);
31093         var curNode = this.root;
31094         if(curNode.attributes[attr] != keys[1]){ // invalid root
31095             if(callback){
31096                 callback(false, null);
31097             }
31098             return;
31099         }
31100         var index = 1;
31101         var f = function(){
31102             if(++index == keys.length){
31103                 if(callback){
31104                     callback(true, curNode);
31105                 }
31106                 return;
31107             }
31108             var c = curNode.findChild(attr, keys[index]);
31109             if(!c){
31110                 if(callback){
31111                     callback(false, curNode);
31112                 }
31113                 return;
31114             }
31115             curNode = c;
31116             c.expand(false, false, f);
31117         };
31118         curNode.expand(false, false, f);
31119     },
31120
31121     /**
31122      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31123      * @param {String} path
31124      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31125      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31126      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31127      */
31128     selectPath : function(path, attr, callback){
31129         attr = attr || "id";
31130         var keys = path.split(this.pathSeparator);
31131         var v = keys.pop();
31132         if(keys.length > 0){
31133             var f = function(success, node){
31134                 if(success && node){
31135                     var n = node.findChild(attr, v);
31136                     if(n){
31137                         n.select();
31138                         if(callback){
31139                             callback(true, n);
31140                         }
31141                     }else if(callback){
31142                         callback(false, n);
31143                     }
31144                 }else{
31145                     if(callback){
31146                         callback(false, n);
31147                     }
31148                 }
31149             };
31150             this.expandPath(keys.join(this.pathSeparator), attr, f);
31151         }else{
31152             this.root.select();
31153             if(callback){
31154                 callback(true, this.root);
31155             }
31156         }
31157     },
31158
31159     getTreeEl : function(){
31160         return this.el;
31161     },
31162
31163     /**
31164      * Trigger rendering of this TreePanel
31165      */
31166     render : function(){
31167         if (this.innerCt) {
31168             return this; // stop it rendering more than once!!
31169         }
31170         
31171         this.innerCt = this.el.createChild({tag:"ul",
31172                cls:"x-tree-root-ct " +
31173                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31174
31175         if(this.containerScroll){
31176             Roo.dd.ScrollManager.register(this.el);
31177         }
31178         if((this.enableDD || this.enableDrop) && !this.dropZone){
31179            /**
31180             * The dropZone used by this tree if drop is enabled
31181             * @type Roo.tree.TreeDropZone
31182             */
31183              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31184                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31185            });
31186         }
31187         if((this.enableDD || this.enableDrag) && !this.dragZone){
31188            /**
31189             * The dragZone used by this tree if drag is enabled
31190             * @type Roo.tree.TreeDragZone
31191             */
31192             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31193                ddGroup: this.ddGroup || "TreeDD",
31194                scroll: this.ddScroll
31195            });
31196         }
31197         this.getSelectionModel().init(this);
31198         if (!this.root) {
31199             console.log("ROOT not set in tree");
31200             return;
31201         }
31202         this.root.render();
31203         if(!this.rootVisible){
31204             this.root.renderChildren();
31205         }
31206         return this;
31207     }
31208 });/*
31209  * Based on:
31210  * Ext JS Library 1.1.1
31211  * Copyright(c) 2006-2007, Ext JS, LLC.
31212  *
31213  * Originally Released Under LGPL - original licence link has changed is not relivant.
31214  *
31215  * Fork - LGPL
31216  * <script type="text/javascript">
31217  */
31218  
31219
31220 /**
31221  * @class Roo.tree.DefaultSelectionModel
31222  * @extends Roo.util.Observable
31223  * The default single selection for a TreePanel.
31224  */
31225 Roo.tree.DefaultSelectionModel = function(){
31226    this.selNode = null;
31227    
31228    this.addEvents({
31229        /**
31230         * @event selectionchange
31231         * Fires when the selected node changes
31232         * @param {DefaultSelectionModel} this
31233         * @param {TreeNode} node the new selection
31234         */
31235        "selectionchange" : true,
31236
31237        /**
31238         * @event beforeselect
31239         * Fires before the selected node changes, return false to cancel the change
31240         * @param {DefaultSelectionModel} this
31241         * @param {TreeNode} node the new selection
31242         * @param {TreeNode} node the old selection
31243         */
31244        "beforeselect" : true
31245    });
31246 };
31247
31248 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31249     init : function(tree){
31250         this.tree = tree;
31251         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31252         tree.on("click", this.onNodeClick, this);
31253     },
31254     
31255     onNodeClick : function(node, e){
31256         if (e.ctrlKey && this.selNode == node)  {
31257             this.unselect(node);
31258             return;
31259         }
31260         this.select(node);
31261     },
31262     
31263     /**
31264      * Select a node.
31265      * @param {TreeNode} node The node to select
31266      * @return {TreeNode} The selected node
31267      */
31268     select : function(node){
31269         var last = this.selNode;
31270         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31271             if(last){
31272                 last.ui.onSelectedChange(false);
31273             }
31274             this.selNode = node;
31275             node.ui.onSelectedChange(true);
31276             this.fireEvent("selectionchange", this, node, last);
31277         }
31278         return node;
31279     },
31280     
31281     /**
31282      * Deselect a node.
31283      * @param {TreeNode} node The node to unselect
31284      */
31285     unselect : function(node){
31286         if(this.selNode == node){
31287             this.clearSelections();
31288         }    
31289     },
31290     
31291     /**
31292      * Clear all selections
31293      */
31294     clearSelections : function(){
31295         var n = this.selNode;
31296         if(n){
31297             n.ui.onSelectedChange(false);
31298             this.selNode = null;
31299             this.fireEvent("selectionchange", this, null);
31300         }
31301         return n;
31302     },
31303     
31304     /**
31305      * Get the selected node
31306      * @return {TreeNode} The selected node
31307      */
31308     getSelectedNode : function(){
31309         return this.selNode;    
31310     },
31311     
31312     /**
31313      * Returns true if the node is selected
31314      * @param {TreeNode} node The node to check
31315      * @return {Boolean}
31316      */
31317     isSelected : function(node){
31318         return this.selNode == node;  
31319     },
31320
31321     /**
31322      * Selects the node above the selected node in the tree, intelligently walking the nodes
31323      * @return TreeNode The new selection
31324      */
31325     selectPrevious : function(){
31326         var s = this.selNode || this.lastSelNode;
31327         if(!s){
31328             return null;
31329         }
31330         var ps = s.previousSibling;
31331         if(ps){
31332             if(!ps.isExpanded() || ps.childNodes.length < 1){
31333                 return this.select(ps);
31334             } else{
31335                 var lc = ps.lastChild;
31336                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31337                     lc = lc.lastChild;
31338                 }
31339                 return this.select(lc);
31340             }
31341         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31342             return this.select(s.parentNode);
31343         }
31344         return null;
31345     },
31346
31347     /**
31348      * Selects the node above the selected node in the tree, intelligently walking the nodes
31349      * @return TreeNode The new selection
31350      */
31351     selectNext : function(){
31352         var s = this.selNode || this.lastSelNode;
31353         if(!s){
31354             return null;
31355         }
31356         if(s.firstChild && s.isExpanded()){
31357              return this.select(s.firstChild);
31358          }else if(s.nextSibling){
31359              return this.select(s.nextSibling);
31360          }else if(s.parentNode){
31361             var newS = null;
31362             s.parentNode.bubble(function(){
31363                 if(this.nextSibling){
31364                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31365                     return false;
31366                 }
31367             });
31368             return newS;
31369          }
31370         return null;
31371     },
31372
31373     onKeyDown : function(e){
31374         var s = this.selNode || this.lastSelNode;
31375         // undesirable, but required
31376         var sm = this;
31377         if(!s){
31378             return;
31379         }
31380         var k = e.getKey();
31381         switch(k){
31382              case e.DOWN:
31383                  e.stopEvent();
31384                  this.selectNext();
31385              break;
31386              case e.UP:
31387                  e.stopEvent();
31388                  this.selectPrevious();
31389              break;
31390              case e.RIGHT:
31391                  e.preventDefault();
31392                  if(s.hasChildNodes()){
31393                      if(!s.isExpanded()){
31394                          s.expand();
31395                      }else if(s.firstChild){
31396                          this.select(s.firstChild, e);
31397                      }
31398                  }
31399              break;
31400              case e.LEFT:
31401                  e.preventDefault();
31402                  if(s.hasChildNodes() && s.isExpanded()){
31403                      s.collapse();
31404                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31405                      this.select(s.parentNode, e);
31406                  }
31407              break;
31408         };
31409     }
31410 });
31411
31412 /**
31413  * @class Roo.tree.MultiSelectionModel
31414  * @extends Roo.util.Observable
31415  * Multi selection for a TreePanel.
31416  */
31417 Roo.tree.MultiSelectionModel = function(){
31418    this.selNodes = [];
31419    this.selMap = {};
31420    this.addEvents({
31421        /**
31422         * @event selectionchange
31423         * Fires when the selected nodes change
31424         * @param {MultiSelectionModel} this
31425         * @param {Array} nodes Array of the selected nodes
31426         */
31427        "selectionchange" : true
31428    });
31429 };
31430
31431 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31432     init : function(tree){
31433         this.tree = tree;
31434         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31435         tree.on("click", this.onNodeClick, this);
31436     },
31437     
31438     onNodeClick : function(node, e){
31439         this.select(node, e, e.ctrlKey);
31440     },
31441     
31442     /**
31443      * Select a node.
31444      * @param {TreeNode} node The node to select
31445      * @param {EventObject} e (optional) An event associated with the selection
31446      * @param {Boolean} keepExisting True to retain existing selections
31447      * @return {TreeNode} The selected node
31448      */
31449     select : function(node, e, keepExisting){
31450         if(keepExisting !== true){
31451             this.clearSelections(true);
31452         }
31453         if(this.isSelected(node)){
31454             this.lastSelNode = node;
31455             return node;
31456         }
31457         this.selNodes.push(node);
31458         this.selMap[node.id] = node;
31459         this.lastSelNode = node;
31460         node.ui.onSelectedChange(true);
31461         this.fireEvent("selectionchange", this, this.selNodes);
31462         return node;
31463     },
31464     
31465     /**
31466      * Deselect a node.
31467      * @param {TreeNode} node The node to unselect
31468      */
31469     unselect : function(node){
31470         if(this.selMap[node.id]){
31471             node.ui.onSelectedChange(false);
31472             var sn = this.selNodes;
31473             var index = -1;
31474             if(sn.indexOf){
31475                 index = sn.indexOf(node);
31476             }else{
31477                 for(var i = 0, len = sn.length; i < len; i++){
31478                     if(sn[i] == node){
31479                         index = i;
31480                         break;
31481                     }
31482                 }
31483             }
31484             if(index != -1){
31485                 this.selNodes.splice(index, 1);
31486             }
31487             delete this.selMap[node.id];
31488             this.fireEvent("selectionchange", this, this.selNodes);
31489         }
31490     },
31491     
31492     /**
31493      * Clear all selections
31494      */
31495     clearSelections : function(suppressEvent){
31496         var sn = this.selNodes;
31497         if(sn.length > 0){
31498             for(var i = 0, len = sn.length; i < len; i++){
31499                 sn[i].ui.onSelectedChange(false);
31500             }
31501             this.selNodes = [];
31502             this.selMap = {};
31503             if(suppressEvent !== true){
31504                 this.fireEvent("selectionchange", this, this.selNodes);
31505             }
31506         }
31507     },
31508     
31509     /**
31510      * Returns true if the node is selected
31511      * @param {TreeNode} node The node to check
31512      * @return {Boolean}
31513      */
31514     isSelected : function(node){
31515         return this.selMap[node.id] ? true : false;  
31516     },
31517     
31518     /**
31519      * Returns an array of the selected nodes
31520      * @return {Array}
31521      */
31522     getSelectedNodes : function(){
31523         return this.selNodes;    
31524     },
31525
31526     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31527
31528     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31529
31530     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31531 });/*
31532  * Based on:
31533  * Ext JS Library 1.1.1
31534  * Copyright(c) 2006-2007, Ext JS, LLC.
31535  *
31536  * Originally Released Under LGPL - original licence link has changed is not relivant.
31537  *
31538  * Fork - LGPL
31539  * <script type="text/javascript">
31540  */
31541  
31542 /**
31543  * @class Roo.tree.TreeNode
31544  * @extends Roo.data.Node
31545  * @cfg {String} text The text for this node
31546  * @cfg {Boolean} expanded true to start the node expanded
31547  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31548  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31549  * @cfg {Boolean} disabled true to start the node disabled
31550  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31551  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31552  * @cfg {String} cls A css class to be added to the node
31553  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31554  * @cfg {String} href URL of the link used for the node (defaults to #)
31555  * @cfg {String} hrefTarget target frame for the link
31556  * @cfg {String} qtip An Ext QuickTip for the node
31557  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31558  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31559  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31560  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31561  * (defaults to undefined with no checkbox rendered)
31562  * @constructor
31563  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31564  */
31565 Roo.tree.TreeNode = function(attributes){
31566     attributes = attributes || {};
31567     if(typeof attributes == "string"){
31568         attributes = {text: attributes};
31569     }
31570     this.childrenRendered = false;
31571     this.rendered = false;
31572     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31573     this.expanded = attributes.expanded === true;
31574     this.isTarget = attributes.isTarget !== false;
31575     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31576     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31577
31578     /**
31579      * Read-only. The text for this node. To change it use setText().
31580      * @type String
31581      */
31582     this.text = attributes.text;
31583     /**
31584      * True if this node is disabled.
31585      * @type Boolean
31586      */
31587     this.disabled = attributes.disabled === true;
31588
31589     this.addEvents({
31590         /**
31591         * @event textchange
31592         * Fires when the text for this node is changed
31593         * @param {Node} this This node
31594         * @param {String} text The new text
31595         * @param {String} oldText The old text
31596         */
31597         "textchange" : true,
31598         /**
31599         * @event beforeexpand
31600         * Fires before this node is expanded, return false to cancel.
31601         * @param {Node} this This node
31602         * @param {Boolean} deep
31603         * @param {Boolean} anim
31604         */
31605         "beforeexpand" : true,
31606         /**
31607         * @event beforecollapse
31608         * Fires before this node is collapsed, return false to cancel.
31609         * @param {Node} this This node
31610         * @param {Boolean} deep
31611         * @param {Boolean} anim
31612         */
31613         "beforecollapse" : true,
31614         /**
31615         * @event expand
31616         * Fires when this node is expanded
31617         * @param {Node} this This node
31618         */
31619         "expand" : true,
31620         /**
31621         * @event disabledchange
31622         * Fires when the disabled status of this node changes
31623         * @param {Node} this This node
31624         * @param {Boolean} disabled
31625         */
31626         "disabledchange" : true,
31627         /**
31628         * @event collapse
31629         * Fires when this node is collapsed
31630         * @param {Node} this This node
31631         */
31632         "collapse" : true,
31633         /**
31634         * @event beforeclick
31635         * Fires before click processing. Return false to cancel the default action.
31636         * @param {Node} this This node
31637         * @param {Roo.EventObject} e The event object
31638         */
31639         "beforeclick":true,
31640         /**
31641         * @event checkchange
31642         * Fires when a node with a checkbox's checked property changes
31643         * @param {Node} this This node
31644         * @param {Boolean} checked
31645         */
31646         "checkchange":true,
31647         /**
31648         * @event click
31649         * Fires when this node is clicked
31650         * @param {Node} this This node
31651         * @param {Roo.EventObject} e The event object
31652         */
31653         "click":true,
31654         /**
31655         * @event dblclick
31656         * Fires when this node is double clicked
31657         * @param {Node} this This node
31658         * @param {Roo.EventObject} e The event object
31659         */
31660         "dblclick":true,
31661         /**
31662         * @event contextmenu
31663         * Fires when this node is right clicked
31664         * @param {Node} this This node
31665         * @param {Roo.EventObject} e The event object
31666         */
31667         "contextmenu":true,
31668         /**
31669         * @event beforechildrenrendered
31670         * Fires right before the child nodes for this node are rendered
31671         * @param {Node} this This node
31672         */
31673         "beforechildrenrendered":true
31674     });
31675
31676     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31677
31678     /**
31679      * Read-only. The UI for this node
31680      * @type TreeNodeUI
31681      */
31682     this.ui = new uiClass(this);
31683 };
31684 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31685     preventHScroll: true,
31686     /**
31687      * Returns true if this node is expanded
31688      * @return {Boolean}
31689      */
31690     isExpanded : function(){
31691         return this.expanded;
31692     },
31693
31694     /**
31695      * Returns the UI object for this node
31696      * @return {TreeNodeUI}
31697      */
31698     getUI : function(){
31699         return this.ui;
31700     },
31701
31702     // private override
31703     setFirstChild : function(node){
31704         var of = this.firstChild;
31705         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31706         if(this.childrenRendered && of && node != of){
31707             of.renderIndent(true, true);
31708         }
31709         if(this.rendered){
31710             this.renderIndent(true, true);
31711         }
31712     },
31713
31714     // private override
31715     setLastChild : function(node){
31716         var ol = this.lastChild;
31717         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31718         if(this.childrenRendered && ol && node != ol){
31719             ol.renderIndent(true, true);
31720         }
31721         if(this.rendered){
31722             this.renderIndent(true, true);
31723         }
31724     },
31725
31726     // these methods are overridden to provide lazy rendering support
31727     // private override
31728     appendChild : function(){
31729         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31730         if(node && this.childrenRendered){
31731             node.render();
31732         }
31733         this.ui.updateExpandIcon();
31734         return node;
31735     },
31736
31737     // private override
31738     removeChild : function(node){
31739         this.ownerTree.getSelectionModel().unselect(node);
31740         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31741         // if it's been rendered remove dom node
31742         if(this.childrenRendered){
31743             node.ui.remove();
31744         }
31745         if(this.childNodes.length < 1){
31746             this.collapse(false, false);
31747         }else{
31748             this.ui.updateExpandIcon();
31749         }
31750         if(!this.firstChild) {
31751             this.childrenRendered = false;
31752         }
31753         return node;
31754     },
31755
31756     // private override
31757     insertBefore : function(node, refNode){
31758         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31759         if(newNode && refNode && this.childrenRendered){
31760             node.render();
31761         }
31762         this.ui.updateExpandIcon();
31763         return newNode;
31764     },
31765
31766     /**
31767      * Sets the text for this node
31768      * @param {String} text
31769      */
31770     setText : function(text){
31771         var oldText = this.text;
31772         this.text = text;
31773         this.attributes.text = text;
31774         if(this.rendered){ // event without subscribing
31775             this.ui.onTextChange(this, text, oldText);
31776         }
31777         this.fireEvent("textchange", this, text, oldText);
31778     },
31779
31780     /**
31781      * Triggers selection of this node
31782      */
31783     select : function(){
31784         this.getOwnerTree().getSelectionModel().select(this);
31785     },
31786
31787     /**
31788      * Triggers deselection of this node
31789      */
31790     unselect : function(){
31791         this.getOwnerTree().getSelectionModel().unselect(this);
31792     },
31793
31794     /**
31795      * Returns true if this node is selected
31796      * @return {Boolean}
31797      */
31798     isSelected : function(){
31799         return this.getOwnerTree().getSelectionModel().isSelected(this);
31800     },
31801
31802     /**
31803      * Expand this node.
31804      * @param {Boolean} deep (optional) True to expand all children as well
31805      * @param {Boolean} anim (optional) false to cancel the default animation
31806      * @param {Function} callback (optional) A callback to be called when
31807      * expanding this node completes (does not wait for deep expand to complete).
31808      * Called with 1 parameter, this node.
31809      */
31810     expand : function(deep, anim, callback){
31811         if(!this.expanded){
31812             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31813                 return;
31814             }
31815             if(!this.childrenRendered){
31816                 this.renderChildren();
31817             }
31818             this.expanded = true;
31819             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31820                 this.ui.animExpand(function(){
31821                     this.fireEvent("expand", this);
31822                     if(typeof callback == "function"){
31823                         callback(this);
31824                     }
31825                     if(deep === true){
31826                         this.expandChildNodes(true);
31827                     }
31828                 }.createDelegate(this));
31829                 return;
31830             }else{
31831                 this.ui.expand();
31832                 this.fireEvent("expand", this);
31833                 if(typeof callback == "function"){
31834                     callback(this);
31835                 }
31836             }
31837         }else{
31838            if(typeof callback == "function"){
31839                callback(this);
31840            }
31841         }
31842         if(deep === true){
31843             this.expandChildNodes(true);
31844         }
31845     },
31846
31847     isHiddenRoot : function(){
31848         return this.isRoot && !this.getOwnerTree().rootVisible;
31849     },
31850
31851     /**
31852      * Collapse this node.
31853      * @param {Boolean} deep (optional) True to collapse all children as well
31854      * @param {Boolean} anim (optional) false to cancel the default animation
31855      */
31856     collapse : function(deep, anim){
31857         if(this.expanded && !this.isHiddenRoot()){
31858             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31859                 return;
31860             }
31861             this.expanded = false;
31862             if((this.getOwnerTree().animate && anim !== false) || anim){
31863                 this.ui.animCollapse(function(){
31864                     this.fireEvent("collapse", this);
31865                     if(deep === true){
31866                         this.collapseChildNodes(true);
31867                     }
31868                 }.createDelegate(this));
31869                 return;
31870             }else{
31871                 this.ui.collapse();
31872                 this.fireEvent("collapse", this);
31873             }
31874         }
31875         if(deep === true){
31876             var cs = this.childNodes;
31877             for(var i = 0, len = cs.length; i < len; i++) {
31878                 cs[i].collapse(true, false);
31879             }
31880         }
31881     },
31882
31883     // private
31884     delayedExpand : function(delay){
31885         if(!this.expandProcId){
31886             this.expandProcId = this.expand.defer(delay, this);
31887         }
31888     },
31889
31890     // private
31891     cancelExpand : function(){
31892         if(this.expandProcId){
31893             clearTimeout(this.expandProcId);
31894         }
31895         this.expandProcId = false;
31896     },
31897
31898     /**
31899      * Toggles expanded/collapsed state of the node
31900      */
31901     toggle : function(){
31902         if(this.expanded){
31903             this.collapse();
31904         }else{
31905             this.expand();
31906         }
31907     },
31908
31909     /**
31910      * Ensures all parent nodes are expanded
31911      */
31912     ensureVisible : function(callback){
31913         var tree = this.getOwnerTree();
31914         tree.expandPath(this.parentNode.getPath(), false, function(){
31915             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31916             Roo.callback(callback);
31917         }.createDelegate(this));
31918     },
31919
31920     /**
31921      * Expand all child nodes
31922      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31923      */
31924     expandChildNodes : function(deep){
31925         var cs = this.childNodes;
31926         for(var i = 0, len = cs.length; i < len; i++) {
31927                 cs[i].expand(deep);
31928         }
31929     },
31930
31931     /**
31932      * Collapse all child nodes
31933      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31934      */
31935     collapseChildNodes : function(deep){
31936         var cs = this.childNodes;
31937         for(var i = 0, len = cs.length; i < len; i++) {
31938                 cs[i].collapse(deep);
31939         }
31940     },
31941
31942     /**
31943      * Disables this node
31944      */
31945     disable : function(){
31946         this.disabled = true;
31947         this.unselect();
31948         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31949             this.ui.onDisableChange(this, true);
31950         }
31951         this.fireEvent("disabledchange", this, true);
31952     },
31953
31954     /**
31955      * Enables this node
31956      */
31957     enable : function(){
31958         this.disabled = false;
31959         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31960             this.ui.onDisableChange(this, false);
31961         }
31962         this.fireEvent("disabledchange", this, false);
31963     },
31964
31965     // private
31966     renderChildren : function(suppressEvent){
31967         if(suppressEvent !== false){
31968             this.fireEvent("beforechildrenrendered", this);
31969         }
31970         var cs = this.childNodes;
31971         for(var i = 0, len = cs.length; i < len; i++){
31972             cs[i].render(true);
31973         }
31974         this.childrenRendered = true;
31975     },
31976
31977     // private
31978     sort : function(fn, scope){
31979         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31980         if(this.childrenRendered){
31981             var cs = this.childNodes;
31982             for(var i = 0, len = cs.length; i < len; i++){
31983                 cs[i].render(true);
31984             }
31985         }
31986     },
31987
31988     // private
31989     render : function(bulkRender){
31990         this.ui.render(bulkRender);
31991         if(!this.rendered){
31992             this.rendered = true;
31993             if(this.expanded){
31994                 this.expanded = false;
31995                 this.expand(false, false);
31996             }
31997         }
31998     },
31999
32000     // private
32001     renderIndent : function(deep, refresh){
32002         if(refresh){
32003             this.ui.childIndent = null;
32004         }
32005         this.ui.renderIndent();
32006         if(deep === true && this.childrenRendered){
32007             var cs = this.childNodes;
32008             for(var i = 0, len = cs.length; i < len; i++){
32009                 cs[i].renderIndent(true, refresh);
32010             }
32011         }
32012     }
32013 });/*
32014  * Based on:
32015  * Ext JS Library 1.1.1
32016  * Copyright(c) 2006-2007, Ext JS, LLC.
32017  *
32018  * Originally Released Under LGPL - original licence link has changed is not relivant.
32019  *
32020  * Fork - LGPL
32021  * <script type="text/javascript">
32022  */
32023  
32024 /**
32025  * @class Roo.tree.AsyncTreeNode
32026  * @extends Roo.tree.TreeNode
32027  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32028  * @constructor
32029  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32030  */
32031  Roo.tree.AsyncTreeNode = function(config){
32032     this.loaded = false;
32033     this.loading = false;
32034     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32035     /**
32036     * @event beforeload
32037     * Fires before this node is loaded, return false to cancel
32038     * @param {Node} this This node
32039     */
32040     this.addEvents({'beforeload':true, 'load': true});
32041     /**
32042     * @event load
32043     * Fires when this node is loaded
32044     * @param {Node} this This node
32045     */
32046     /**
32047      * The loader used by this node (defaults to using the tree's defined loader)
32048      * @type TreeLoader
32049      * @property loader
32050      */
32051 };
32052 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32053     expand : function(deep, anim, callback){
32054         if(this.loading){ // if an async load is already running, waiting til it's done
32055             var timer;
32056             var f = function(){
32057                 if(!this.loading){ // done loading
32058                     clearInterval(timer);
32059                     this.expand(deep, anim, callback);
32060                 }
32061             }.createDelegate(this);
32062             timer = setInterval(f, 200);
32063             return;
32064         }
32065         if(!this.loaded){
32066             if(this.fireEvent("beforeload", this) === false){
32067                 return;
32068             }
32069             this.loading = true;
32070             this.ui.beforeLoad(this);
32071             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32072             if(loader){
32073                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32074                 return;
32075             }
32076         }
32077         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32078     },
32079     
32080     /**
32081      * Returns true if this node is currently loading
32082      * @return {Boolean}
32083      */
32084     isLoading : function(){
32085         return this.loading;  
32086     },
32087     
32088     loadComplete : function(deep, anim, callback){
32089         this.loading = false;
32090         this.loaded = true;
32091         this.ui.afterLoad(this);
32092         this.fireEvent("load", this);
32093         this.expand(deep, anim, callback);
32094     },
32095     
32096     /**
32097      * Returns true if this node has been loaded
32098      * @return {Boolean}
32099      */
32100     isLoaded : function(){
32101         return this.loaded;
32102     },
32103     
32104     hasChildNodes : function(){
32105         if(!this.isLeaf() && !this.loaded){
32106             return true;
32107         }else{
32108             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32109         }
32110     },
32111
32112     /**
32113      * Trigger a reload for this node
32114      * @param {Function} callback
32115      */
32116     reload : function(callback){
32117         this.collapse(false, false);
32118         while(this.firstChild){
32119             this.removeChild(this.firstChild);
32120         }
32121         this.childrenRendered = false;
32122         this.loaded = false;
32123         if(this.isHiddenRoot()){
32124             this.expanded = false;
32125         }
32126         this.expand(false, false, callback);
32127     }
32128 });/*
32129  * Based on:
32130  * Ext JS Library 1.1.1
32131  * Copyright(c) 2006-2007, Ext JS, LLC.
32132  *
32133  * Originally Released Under LGPL - original licence link has changed is not relivant.
32134  *
32135  * Fork - LGPL
32136  * <script type="text/javascript">
32137  */
32138  
32139 /**
32140  * @class Roo.tree.TreeNodeUI
32141  * @constructor
32142  * @param {Object} node The node to render
32143  * The TreeNode UI implementation is separate from the
32144  * tree implementation. Unless you are customizing the tree UI,
32145  * you should never have to use this directly.
32146  */
32147 Roo.tree.TreeNodeUI = function(node){
32148     this.node = node;
32149     this.rendered = false;
32150     this.animating = false;
32151     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32152 };
32153
32154 Roo.tree.TreeNodeUI.prototype = {
32155     removeChild : function(node){
32156         if(this.rendered){
32157             this.ctNode.removeChild(node.ui.getEl());
32158         }
32159     },
32160
32161     beforeLoad : function(){
32162          this.addClass("x-tree-node-loading");
32163     },
32164
32165     afterLoad : function(){
32166          this.removeClass("x-tree-node-loading");
32167     },
32168
32169     onTextChange : function(node, text, oldText){
32170         if(this.rendered){
32171             this.textNode.innerHTML = text;
32172         }
32173     },
32174
32175     onDisableChange : function(node, state){
32176         this.disabled = state;
32177         if(state){
32178             this.addClass("x-tree-node-disabled");
32179         }else{
32180             this.removeClass("x-tree-node-disabled");
32181         }
32182     },
32183
32184     onSelectedChange : function(state){
32185         if(state){
32186             this.focus();
32187             this.addClass("x-tree-selected");
32188         }else{
32189             //this.blur();
32190             this.removeClass("x-tree-selected");
32191         }
32192     },
32193
32194     onMove : function(tree, node, oldParent, newParent, index, refNode){
32195         this.childIndent = null;
32196         if(this.rendered){
32197             var targetNode = newParent.ui.getContainer();
32198             if(!targetNode){//target not rendered
32199                 this.holder = document.createElement("div");
32200                 this.holder.appendChild(this.wrap);
32201                 return;
32202             }
32203             var insertBefore = refNode ? refNode.ui.getEl() : null;
32204             if(insertBefore){
32205                 targetNode.insertBefore(this.wrap, insertBefore);
32206             }else{
32207                 targetNode.appendChild(this.wrap);
32208             }
32209             this.node.renderIndent(true);
32210         }
32211     },
32212
32213     addClass : function(cls){
32214         if(this.elNode){
32215             Roo.fly(this.elNode).addClass(cls);
32216         }
32217     },
32218
32219     removeClass : function(cls){
32220         if(this.elNode){
32221             Roo.fly(this.elNode).removeClass(cls);
32222         }
32223     },
32224
32225     remove : function(){
32226         if(this.rendered){
32227             this.holder = document.createElement("div");
32228             this.holder.appendChild(this.wrap);
32229         }
32230     },
32231
32232     fireEvent : function(){
32233         return this.node.fireEvent.apply(this.node, arguments);
32234     },
32235
32236     initEvents : function(){
32237         this.node.on("move", this.onMove, this);
32238         var E = Roo.EventManager;
32239         var a = this.anchor;
32240
32241         var el = Roo.fly(a, '_treeui');
32242
32243         if(Roo.isOpera){ // opera render bug ignores the CSS
32244             el.setStyle("text-decoration", "none");
32245         }
32246
32247         el.on("click", this.onClick, this);
32248         el.on("dblclick", this.onDblClick, this);
32249
32250         if(this.checkbox){
32251             Roo.EventManager.on(this.checkbox,
32252                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32253         }
32254
32255         el.on("contextmenu", this.onContextMenu, this);
32256
32257         var icon = Roo.fly(this.iconNode);
32258         icon.on("click", this.onClick, this);
32259         icon.on("dblclick", this.onDblClick, this);
32260         icon.on("contextmenu", this.onContextMenu, this);
32261         E.on(this.ecNode, "click", this.ecClick, this, true);
32262
32263         if(this.node.disabled){
32264             this.addClass("x-tree-node-disabled");
32265         }
32266         if(this.node.hidden){
32267             this.addClass("x-tree-node-disabled");
32268         }
32269         var ot = this.node.getOwnerTree();
32270         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32271         if(dd && (!this.node.isRoot || ot.rootVisible)){
32272             Roo.dd.Registry.register(this.elNode, {
32273                 node: this.node,
32274                 handles: this.getDDHandles(),
32275                 isHandle: false
32276             });
32277         }
32278     },
32279
32280     getDDHandles : function(){
32281         return [this.iconNode, this.textNode];
32282     },
32283
32284     hide : function(){
32285         if(this.rendered){
32286             this.wrap.style.display = "none";
32287         }
32288     },
32289
32290     show : function(){
32291         if(this.rendered){
32292             this.wrap.style.display = "";
32293         }
32294     },
32295
32296     onContextMenu : function(e){
32297         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32298             e.preventDefault();
32299             this.focus();
32300             this.fireEvent("contextmenu", this.node, e);
32301         }
32302     },
32303
32304     onClick : function(e){
32305         if(this.dropping){
32306             e.stopEvent();
32307             return;
32308         }
32309         if(this.fireEvent("beforeclick", this.node, e) !== false){
32310             if(!this.disabled && this.node.attributes.href){
32311                 this.fireEvent("click", this.node, e);
32312                 return;
32313             }
32314             e.preventDefault();
32315             if(this.disabled){
32316                 return;
32317             }
32318
32319             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32320                 this.node.toggle();
32321             }
32322
32323             this.fireEvent("click", this.node, e);
32324         }else{
32325             e.stopEvent();
32326         }
32327     },
32328
32329     onDblClick : function(e){
32330         e.preventDefault();
32331         if(this.disabled){
32332             return;
32333         }
32334         if(this.checkbox){
32335             this.toggleCheck();
32336         }
32337         if(!this.animating && this.node.hasChildNodes()){
32338             this.node.toggle();
32339         }
32340         this.fireEvent("dblclick", this.node, e);
32341     },
32342
32343     onCheckChange : function(){
32344         var checked = this.checkbox.checked;
32345         this.node.attributes.checked = checked;
32346         this.fireEvent('checkchange', this.node, checked);
32347     },
32348
32349     ecClick : function(e){
32350         if(!this.animating && this.node.hasChildNodes()){
32351             this.node.toggle();
32352         }
32353     },
32354
32355     startDrop : function(){
32356         this.dropping = true;
32357     },
32358
32359     // delayed drop so the click event doesn't get fired on a drop
32360     endDrop : function(){
32361        setTimeout(function(){
32362            this.dropping = false;
32363        }.createDelegate(this), 50);
32364     },
32365
32366     expand : function(){
32367         this.updateExpandIcon();
32368         this.ctNode.style.display = "";
32369     },
32370
32371     focus : function(){
32372         if(!this.node.preventHScroll){
32373             try{this.anchor.focus();
32374             }catch(e){}
32375         }else if(!Roo.isIE){
32376             try{
32377                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32378                 var l = noscroll.scrollLeft;
32379                 this.anchor.focus();
32380                 noscroll.scrollLeft = l;
32381             }catch(e){}
32382         }
32383     },
32384
32385     toggleCheck : function(value){
32386         var cb = this.checkbox;
32387         if(cb){
32388             cb.checked = (value === undefined ? !cb.checked : value);
32389         }
32390     },
32391
32392     blur : function(){
32393         try{
32394             this.anchor.blur();
32395         }catch(e){}
32396     },
32397
32398     animExpand : function(callback){
32399         var ct = Roo.get(this.ctNode);
32400         ct.stopFx();
32401         if(!this.node.hasChildNodes()){
32402             this.updateExpandIcon();
32403             this.ctNode.style.display = "";
32404             Roo.callback(callback);
32405             return;
32406         }
32407         this.animating = true;
32408         this.updateExpandIcon();
32409
32410         ct.slideIn('t', {
32411            callback : function(){
32412                this.animating = false;
32413                Roo.callback(callback);
32414             },
32415             scope: this,
32416             duration: this.node.ownerTree.duration || .25
32417         });
32418     },
32419
32420     highlight : function(){
32421         var tree = this.node.getOwnerTree();
32422         Roo.fly(this.wrap).highlight(
32423             tree.hlColor || "C3DAF9",
32424             {endColor: tree.hlBaseColor}
32425         );
32426     },
32427
32428     collapse : function(){
32429         this.updateExpandIcon();
32430         this.ctNode.style.display = "none";
32431     },
32432
32433     animCollapse : function(callback){
32434         var ct = Roo.get(this.ctNode);
32435         ct.enableDisplayMode('block');
32436         ct.stopFx();
32437
32438         this.animating = true;
32439         this.updateExpandIcon();
32440
32441         ct.slideOut('t', {
32442             callback : function(){
32443                this.animating = false;
32444                Roo.callback(callback);
32445             },
32446             scope: this,
32447             duration: this.node.ownerTree.duration || .25
32448         });
32449     },
32450
32451     getContainer : function(){
32452         return this.ctNode;
32453     },
32454
32455     getEl : function(){
32456         return this.wrap;
32457     },
32458
32459     appendDDGhost : function(ghostNode){
32460         ghostNode.appendChild(this.elNode.cloneNode(true));
32461     },
32462
32463     getDDRepairXY : function(){
32464         return Roo.lib.Dom.getXY(this.iconNode);
32465     },
32466
32467     onRender : function(){
32468         this.render();
32469     },
32470
32471     render : function(bulkRender){
32472         var n = this.node, a = n.attributes;
32473         var targetNode = n.parentNode ?
32474               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32475
32476         if(!this.rendered){
32477             this.rendered = true;
32478
32479             this.renderElements(n, a, targetNode, bulkRender);
32480
32481             if(a.qtip){
32482                if(this.textNode.setAttributeNS){
32483                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32484                    if(a.qtipTitle){
32485                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32486                    }
32487                }else{
32488                    this.textNode.setAttribute("ext:qtip", a.qtip);
32489                    if(a.qtipTitle){
32490                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32491                    }
32492                }
32493             }else if(a.qtipCfg){
32494                 a.qtipCfg.target = Roo.id(this.textNode);
32495                 Roo.QuickTips.register(a.qtipCfg);
32496             }
32497             this.initEvents();
32498             if(!this.node.expanded){
32499                 this.updateExpandIcon();
32500             }
32501         }else{
32502             if(bulkRender === true) {
32503                 targetNode.appendChild(this.wrap);
32504             }
32505         }
32506     },
32507
32508     renderElements : function(n, a, targetNode, bulkRender){
32509         // add some indent caching, this helps performance when rendering a large tree
32510         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32511         var t = n.getOwnerTree();
32512         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32513         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32514         var cb = typeof a.checked == 'boolean';
32515         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32516         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32517             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32518             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32519             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32520             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32521             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32522              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32523                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32524             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32525             "</li>"];
32526
32527         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32528             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32529                                 n.nextSibling.ui.getEl(), buf.join(""));
32530         }else{
32531             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32532         }
32533
32534         this.elNode = this.wrap.childNodes[0];
32535         this.ctNode = this.wrap.childNodes[1];
32536         var cs = this.elNode.childNodes;
32537         this.indentNode = cs[0];
32538         this.ecNode = cs[1];
32539         this.iconNode = cs[2];
32540         var index = 3;
32541         if(cb){
32542             this.checkbox = cs[3];
32543             index++;
32544         }
32545         this.anchor = cs[index];
32546         this.textNode = cs[index].firstChild;
32547     },
32548
32549     getAnchor : function(){
32550         return this.anchor;
32551     },
32552
32553     getTextEl : function(){
32554         return this.textNode;
32555     },
32556
32557     getIconEl : function(){
32558         return this.iconNode;
32559     },
32560
32561     isChecked : function(){
32562         return this.checkbox ? this.checkbox.checked : false;
32563     },
32564
32565     updateExpandIcon : function(){
32566         if(this.rendered){
32567             var n = this.node, c1, c2;
32568             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32569             var hasChild = n.hasChildNodes();
32570             if(hasChild){
32571                 if(n.expanded){
32572                     cls += "-minus";
32573                     c1 = "x-tree-node-collapsed";
32574                     c2 = "x-tree-node-expanded";
32575                 }else{
32576                     cls += "-plus";
32577                     c1 = "x-tree-node-expanded";
32578                     c2 = "x-tree-node-collapsed";
32579                 }
32580                 if(this.wasLeaf){
32581                     this.removeClass("x-tree-node-leaf");
32582                     this.wasLeaf = false;
32583                 }
32584                 if(this.c1 != c1 || this.c2 != c2){
32585                     Roo.fly(this.elNode).replaceClass(c1, c2);
32586                     this.c1 = c1; this.c2 = c2;
32587                 }
32588             }else{
32589                 if(!this.wasLeaf){
32590                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32591                     delete this.c1;
32592                     delete this.c2;
32593                     this.wasLeaf = true;
32594                 }
32595             }
32596             var ecc = "x-tree-ec-icon "+cls;
32597             if(this.ecc != ecc){
32598                 this.ecNode.className = ecc;
32599                 this.ecc = ecc;
32600             }
32601         }
32602     },
32603
32604     getChildIndent : function(){
32605         if(!this.childIndent){
32606             var buf = [];
32607             var p = this.node;
32608             while(p){
32609                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32610                     if(!p.isLast()) {
32611                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32612                     } else {
32613                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32614                     }
32615                 }
32616                 p = p.parentNode;
32617             }
32618             this.childIndent = buf.join("");
32619         }
32620         return this.childIndent;
32621     },
32622
32623     renderIndent : function(){
32624         if(this.rendered){
32625             var indent = "";
32626             var p = this.node.parentNode;
32627             if(p){
32628                 indent = p.ui.getChildIndent();
32629             }
32630             if(this.indentMarkup != indent){ // don't rerender if not required
32631                 this.indentNode.innerHTML = indent;
32632                 this.indentMarkup = indent;
32633             }
32634             this.updateExpandIcon();
32635         }
32636     }
32637 };
32638
32639 Roo.tree.RootTreeNodeUI = function(){
32640     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32641 };
32642 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32643     render : function(){
32644         if(!this.rendered){
32645             var targetNode = this.node.ownerTree.innerCt.dom;
32646             this.node.expanded = true;
32647             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32648             this.wrap = this.ctNode = targetNode.firstChild;
32649         }
32650     },
32651     collapse : function(){
32652     },
32653     expand : function(){
32654     }
32655 });/*
32656  * Based on:
32657  * Ext JS Library 1.1.1
32658  * Copyright(c) 2006-2007, Ext JS, LLC.
32659  *
32660  * Originally Released Under LGPL - original licence link has changed is not relivant.
32661  *
32662  * Fork - LGPL
32663  * <script type="text/javascript">
32664  */
32665 /**
32666  * @class Roo.tree.TreeLoader
32667  * @extends Roo.util.Observable
32668  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32669  * nodes from a specified URL. The response must be a javascript Array definition
32670  * who's elements are node definition objects. eg:
32671  * <pre><code>
32672    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32673     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32674 </code></pre>
32675  * <br><br>
32676  * A server request is sent, and child nodes are loaded only when a node is expanded.
32677  * The loading node's id is passed to the server under the parameter name "node" to
32678  * enable the server to produce the correct child nodes.
32679  * <br><br>
32680  * To pass extra parameters, an event handler may be attached to the "beforeload"
32681  * event, and the parameters specified in the TreeLoader's baseParams property:
32682  * <pre><code>
32683     myTreeLoader.on("beforeload", function(treeLoader, node) {
32684         this.baseParams.category = node.attributes.category;
32685     }, this);
32686 </code></pre><
32687  * This would pass an HTTP parameter called "category" to the server containing
32688  * the value of the Node's "category" attribute.
32689  * @constructor
32690  * Creates a new Treeloader.
32691  * @param {Object} config A config object containing config properties.
32692  */
32693 Roo.tree.TreeLoader = function(config){
32694     this.baseParams = {};
32695     this.requestMethod = "POST";
32696     Roo.apply(this, config);
32697
32698     this.addEvents({
32699     
32700         /**
32701          * @event beforeload
32702          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32703          * @param {Object} This TreeLoader object.
32704          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32705          * @param {Object} callback The callback function specified in the {@link #load} call.
32706          */
32707         beforeload : true,
32708         /**
32709          * @event load
32710          * Fires when the node has been successfuly loaded.
32711          * @param {Object} This TreeLoader object.
32712          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32713          * @param {Object} response The response object containing the data from the server.
32714          */
32715         load : true,
32716         /**
32717          * @event loadexception
32718          * Fires if the network request failed.
32719          * @param {Object} This TreeLoader object.
32720          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32721          * @param {Object} response The response object containing the data from the server.
32722          */
32723         loadexception : true,
32724         /**
32725          * @event create
32726          * Fires before a node is created, enabling you to return custom Node types 
32727          * @param {Object} This TreeLoader object.
32728          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32729          */
32730         create : true
32731     });
32732
32733     Roo.tree.TreeLoader.superclass.constructor.call(this);
32734 };
32735
32736 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32737     /**
32738     * @cfg {String} dataUrl The URL from which to request a Json string which
32739     * specifies an array of node definition object representing the child nodes
32740     * to be loaded.
32741     */
32742     /**
32743     * @cfg {Object} baseParams (optional) An object containing properties which
32744     * specify HTTP parameters to be passed to each request for child nodes.
32745     */
32746     /**
32747     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32748     * created by this loader. If the attributes sent by the server have an attribute in this object,
32749     * they take priority.
32750     */
32751     /**
32752     * @cfg {Object} uiProviders (optional) An object containing properties which
32753     * 
32754     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32755     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32756     * <i>uiProvider</i> attribute of a returned child node is a string rather
32757     * than a reference to a TreeNodeUI implementation, this that string value
32758     * is used as a property name in the uiProviders object. You can define the provider named
32759     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32760     */
32761     uiProviders : {},
32762
32763     /**
32764     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32765     * child nodes before loading.
32766     */
32767     clearOnLoad : true,
32768
32769     /**
32770     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32771     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32772     * Grid query { data : [ .....] }
32773     */
32774     
32775     root : false,
32776      /**
32777     * @cfg {String} queryParam (optional) 
32778     * Name of the query as it will be passed on the querystring (defaults to 'node')
32779     * eg. the request will be ?node=[id]
32780     */
32781     
32782     
32783     queryParam: false,
32784     
32785     /**
32786      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32787      * This is called automatically when a node is expanded, but may be used to reload
32788      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32789      * @param {Roo.tree.TreeNode} node
32790      * @param {Function} callback
32791      */
32792     load : function(node, callback){
32793         if(this.clearOnLoad){
32794             while(node.firstChild){
32795                 node.removeChild(node.firstChild);
32796             }
32797         }
32798         if(node.attributes.children){ // preloaded json children
32799             var cs = node.attributes.children;
32800             for(var i = 0, len = cs.length; i < len; i++){
32801                 node.appendChild(this.createNode(cs[i]));
32802             }
32803             if(typeof callback == "function"){
32804                 callback();
32805             }
32806         }else if(this.dataUrl){
32807             this.requestData(node, callback);
32808         }
32809     },
32810
32811     getParams: function(node){
32812         var buf = [], bp = this.baseParams;
32813         for(var key in bp){
32814             if(typeof bp[key] != "function"){
32815                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32816             }
32817         }
32818         var n = this.queryParam === false ? 'node' : this.queryParam;
32819         buf.push(n + "=", encodeURIComponent(node.id));
32820         return buf.join("");
32821     },
32822
32823     requestData : function(node, callback){
32824         if(this.fireEvent("beforeload", this, node, callback) !== false){
32825             this.transId = Roo.Ajax.request({
32826                 method:this.requestMethod,
32827                 url: this.dataUrl||this.url,
32828                 success: this.handleResponse,
32829                 failure: this.handleFailure,
32830                 scope: this,
32831                 argument: {callback: callback, node: node},
32832                 params: this.getParams(node)
32833             });
32834         }else{
32835             // if the load is cancelled, make sure we notify
32836             // the node that we are done
32837             if(typeof callback == "function"){
32838                 callback();
32839             }
32840         }
32841     },
32842
32843     isLoading : function(){
32844         return this.transId ? true : false;
32845     },
32846
32847     abort : function(){
32848         if(this.isLoading()){
32849             Roo.Ajax.abort(this.transId);
32850         }
32851     },
32852
32853     // private
32854     createNode : function(attr){
32855         // apply baseAttrs, nice idea Corey!
32856         if(this.baseAttrs){
32857             Roo.applyIf(attr, this.baseAttrs);
32858         }
32859         if(this.applyLoader !== false){
32860             attr.loader = this;
32861         }
32862         // uiProvider = depreciated..
32863         
32864         if(typeof(attr.uiProvider) == 'string'){
32865            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32866                 /**  eval:var:attr */ eval(attr.uiProvider);
32867         }
32868         if(typeof(this.uiProviders['default']) != 'undefined') {
32869             attr.uiProvider = this.uiProviders['default'];
32870         }
32871         
32872         this.fireEvent('create', this, attr);
32873         
32874         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32875         return(attr.leaf ?
32876                         new Roo.tree.TreeNode(attr) :
32877                         new Roo.tree.AsyncTreeNode(attr));
32878     },
32879
32880     processResponse : function(response, node, callback){
32881         var json = response.responseText;
32882         try {
32883             
32884             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32885             if (this.root !== false) {
32886                 o = o[this.root];
32887             }
32888             
32889             for(var i = 0, len = o.length; i < len; i++){
32890                 var n = this.createNode(o[i]);
32891                 if(n){
32892                     node.appendChild(n);
32893                 }
32894             }
32895             if(typeof callback == "function"){
32896                 callback(this, node);
32897             }
32898         }catch(e){
32899             this.handleFailure(response);
32900         }
32901     },
32902
32903     handleResponse : function(response){
32904         this.transId = false;
32905         var a = response.argument;
32906         this.processResponse(response, a.node, a.callback);
32907         this.fireEvent("load", this, a.node, response);
32908     },
32909
32910     handleFailure : function(response){
32911         this.transId = false;
32912         var a = response.argument;
32913         this.fireEvent("loadexception", this, a.node, response);
32914         if(typeof a.callback == "function"){
32915             a.callback(this, a.node);
32916         }
32917     }
32918 });/*
32919  * Based on:
32920  * Ext JS Library 1.1.1
32921  * Copyright(c) 2006-2007, Ext JS, LLC.
32922  *
32923  * Originally Released Under LGPL - original licence link has changed is not relivant.
32924  *
32925  * Fork - LGPL
32926  * <script type="text/javascript">
32927  */
32928
32929 /**
32930 * @class Roo.tree.TreeFilter
32931 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32932 * @param {TreePanel} tree
32933 * @param {Object} config (optional)
32934  */
32935 Roo.tree.TreeFilter = function(tree, config){
32936     this.tree = tree;
32937     this.filtered = {};
32938     Roo.apply(this, config);
32939 };
32940
32941 Roo.tree.TreeFilter.prototype = {
32942     clearBlank:false,
32943     reverse:false,
32944     autoClear:false,
32945     remove:false,
32946
32947      /**
32948      * Filter the data by a specific attribute.
32949      * @param {String/RegExp} value Either string that the attribute value
32950      * should start with or a RegExp to test against the attribute
32951      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32952      * @param {TreeNode} startNode (optional) The node to start the filter at.
32953      */
32954     filter : function(value, attr, startNode){
32955         attr = attr || "text";
32956         var f;
32957         if(typeof value == "string"){
32958             var vlen = value.length;
32959             // auto clear empty filter
32960             if(vlen == 0 && this.clearBlank){
32961                 this.clear();
32962                 return;
32963             }
32964             value = value.toLowerCase();
32965             f = function(n){
32966                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32967             };
32968         }else if(value.exec){ // regex?
32969             f = function(n){
32970                 return value.test(n.attributes[attr]);
32971             };
32972         }else{
32973             throw 'Illegal filter type, must be string or regex';
32974         }
32975         this.filterBy(f, null, startNode);
32976         },
32977
32978     /**
32979      * Filter by a function. The passed function will be called with each
32980      * node in the tree (or from the startNode). If the function returns true, the node is kept
32981      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32982      * @param {Function} fn The filter function
32983      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32984      */
32985     filterBy : function(fn, scope, startNode){
32986         startNode = startNode || this.tree.root;
32987         if(this.autoClear){
32988             this.clear();
32989         }
32990         var af = this.filtered, rv = this.reverse;
32991         var f = function(n){
32992             if(n == startNode){
32993                 return true;
32994             }
32995             if(af[n.id]){
32996                 return false;
32997             }
32998             var m = fn.call(scope || n, n);
32999             if(!m || rv){
33000                 af[n.id] = n;
33001                 n.ui.hide();
33002                 return false;
33003             }
33004             return true;
33005         };
33006         startNode.cascade(f);
33007         if(this.remove){
33008            for(var id in af){
33009                if(typeof id != "function"){
33010                    var n = af[id];
33011                    if(n && n.parentNode){
33012                        n.parentNode.removeChild(n);
33013                    }
33014                }
33015            }
33016         }
33017     },
33018
33019     /**
33020      * Clears the current filter. Note: with the "remove" option
33021      * set a filter cannot be cleared.
33022      */
33023     clear : function(){
33024         var t = this.tree;
33025         var af = this.filtered;
33026         for(var id in af){
33027             if(typeof id != "function"){
33028                 var n = af[id];
33029                 if(n){
33030                     n.ui.show();
33031                 }
33032             }
33033         }
33034         this.filtered = {};
33035     }
33036 };
33037 /*
33038  * Based on:
33039  * Ext JS Library 1.1.1
33040  * Copyright(c) 2006-2007, Ext JS, LLC.
33041  *
33042  * Originally Released Under LGPL - original licence link has changed is not relivant.
33043  *
33044  * Fork - LGPL
33045  * <script type="text/javascript">
33046  */
33047  
33048
33049 /**
33050  * @class Roo.tree.TreeSorter
33051  * Provides sorting of nodes in a TreePanel
33052  * 
33053  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33054  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33055  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33056  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33057  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33058  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33059  * @constructor
33060  * @param {TreePanel} tree
33061  * @param {Object} config
33062  */
33063 Roo.tree.TreeSorter = function(tree, config){
33064     Roo.apply(this, config);
33065     tree.on("beforechildrenrendered", this.doSort, this);
33066     tree.on("append", this.updateSort, this);
33067     tree.on("insert", this.updateSort, this);
33068     
33069     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33070     var p = this.property || "text";
33071     var sortType = this.sortType;
33072     var fs = this.folderSort;
33073     var cs = this.caseSensitive === true;
33074     var leafAttr = this.leafAttr || 'leaf';
33075
33076     this.sortFn = function(n1, n2){
33077         if(fs){
33078             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33079                 return 1;
33080             }
33081             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33082                 return -1;
33083             }
33084         }
33085         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33086         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33087         if(v1 < v2){
33088                         return dsc ? +1 : -1;
33089                 }else if(v1 > v2){
33090                         return dsc ? -1 : +1;
33091         }else{
33092                 return 0;
33093         }
33094     };
33095 };
33096
33097 Roo.tree.TreeSorter.prototype = {
33098     doSort : function(node){
33099         node.sort(this.sortFn);
33100     },
33101     
33102     compareNodes : function(n1, n2){
33103         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33104     },
33105     
33106     updateSort : function(tree, node){
33107         if(node.childrenRendered){
33108             this.doSort.defer(1, this, [node]);
33109         }
33110     }
33111 };/*
33112  * Based on:
33113  * Ext JS Library 1.1.1
33114  * Copyright(c) 2006-2007, Ext JS, LLC.
33115  *
33116  * Originally Released Under LGPL - original licence link has changed is not relivant.
33117  *
33118  * Fork - LGPL
33119  * <script type="text/javascript">
33120  */
33121
33122 if(Roo.dd.DropZone){
33123     
33124 Roo.tree.TreeDropZone = function(tree, config){
33125     this.allowParentInsert = false;
33126     this.allowContainerDrop = false;
33127     this.appendOnly = false;
33128     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33129     this.tree = tree;
33130     this.lastInsertClass = "x-tree-no-status";
33131     this.dragOverData = {};
33132 };
33133
33134 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33135     ddGroup : "TreeDD",
33136     
33137     expandDelay : 1000,
33138     
33139     expandNode : function(node){
33140         if(node.hasChildNodes() && !node.isExpanded()){
33141             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33142         }
33143     },
33144     
33145     queueExpand : function(node){
33146         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33147     },
33148     
33149     cancelExpand : function(){
33150         if(this.expandProcId){
33151             clearTimeout(this.expandProcId);
33152             this.expandProcId = false;
33153         }
33154     },
33155     
33156     isValidDropPoint : function(n, pt, dd, e, data){
33157         if(!n || !data){ return false; }
33158         var targetNode = n.node;
33159         var dropNode = data.node;
33160         // default drop rules
33161         if(!(targetNode && targetNode.isTarget && pt)){
33162             return false;
33163         }
33164         if(pt == "append" && targetNode.allowChildren === false){
33165             return false;
33166         }
33167         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33168             return false;
33169         }
33170         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33171             return false;
33172         }
33173         // reuse the object
33174         var overEvent = this.dragOverData;
33175         overEvent.tree = this.tree;
33176         overEvent.target = targetNode;
33177         overEvent.data = data;
33178         overEvent.point = pt;
33179         overEvent.source = dd;
33180         overEvent.rawEvent = e;
33181         overEvent.dropNode = dropNode;
33182         overEvent.cancel = false;  
33183         var result = this.tree.fireEvent("nodedragover", overEvent);
33184         return overEvent.cancel === false && result !== false;
33185     },
33186     
33187     getDropPoint : function(e, n, dd){
33188         var tn = n.node;
33189         if(tn.isRoot){
33190             return tn.allowChildren !== false ? "append" : false; // always append for root
33191         }
33192         var dragEl = n.ddel;
33193         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33194         var y = Roo.lib.Event.getPageY(e);
33195         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33196         
33197         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33198         var noAppend = tn.allowChildren === false;
33199         if(this.appendOnly || tn.parentNode.allowChildren === false){
33200             return noAppend ? false : "append";
33201         }
33202         var noBelow = false;
33203         if(!this.allowParentInsert){
33204             noBelow = tn.hasChildNodes() && tn.isExpanded();
33205         }
33206         var q = (b - t) / (noAppend ? 2 : 3);
33207         if(y >= t && y < (t + q)){
33208             return "above";
33209         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33210             return "below";
33211         }else{
33212             return "append";
33213         }
33214     },
33215     
33216     onNodeEnter : function(n, dd, e, data){
33217         this.cancelExpand();
33218     },
33219     
33220     onNodeOver : function(n, dd, e, data){
33221         var pt = this.getDropPoint(e, n, dd);
33222         var node = n.node;
33223         
33224         // auto node expand check
33225         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33226             this.queueExpand(node);
33227         }else if(pt != "append"){
33228             this.cancelExpand();
33229         }
33230         
33231         // set the insert point style on the target node
33232         var returnCls = this.dropNotAllowed;
33233         if(this.isValidDropPoint(n, pt, dd, e, data)){
33234            if(pt){
33235                var el = n.ddel;
33236                var cls;
33237                if(pt == "above"){
33238                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33239                    cls = "x-tree-drag-insert-above";
33240                }else if(pt == "below"){
33241                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33242                    cls = "x-tree-drag-insert-below";
33243                }else{
33244                    returnCls = "x-tree-drop-ok-append";
33245                    cls = "x-tree-drag-append";
33246                }
33247                if(this.lastInsertClass != cls){
33248                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33249                    this.lastInsertClass = cls;
33250                }
33251            }
33252        }
33253        return returnCls;
33254     },
33255     
33256     onNodeOut : function(n, dd, e, data){
33257         this.cancelExpand();
33258         this.removeDropIndicators(n);
33259     },
33260     
33261     onNodeDrop : function(n, dd, e, data){
33262         var point = this.getDropPoint(e, n, dd);
33263         var targetNode = n.node;
33264         targetNode.ui.startDrop();
33265         if(!this.isValidDropPoint(n, point, dd, e, data)){
33266             targetNode.ui.endDrop();
33267             return false;
33268         }
33269         // first try to find the drop node
33270         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33271         var dropEvent = {
33272             tree : this.tree,
33273             target: targetNode,
33274             data: data,
33275             point: point,
33276             source: dd,
33277             rawEvent: e,
33278             dropNode: dropNode,
33279             cancel: !dropNode   
33280         };
33281         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33282         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33283             targetNode.ui.endDrop();
33284             return false;
33285         }
33286         // allow target changing
33287         targetNode = dropEvent.target;
33288         if(point == "append" && !targetNode.isExpanded()){
33289             targetNode.expand(false, null, function(){
33290                 this.completeDrop(dropEvent);
33291             }.createDelegate(this));
33292         }else{
33293             this.completeDrop(dropEvent);
33294         }
33295         return true;
33296     },
33297     
33298     completeDrop : function(de){
33299         var ns = de.dropNode, p = de.point, t = de.target;
33300         if(!(ns instanceof Array)){
33301             ns = [ns];
33302         }
33303         var n;
33304         for(var i = 0, len = ns.length; i < len; i++){
33305             n = ns[i];
33306             if(p == "above"){
33307                 t.parentNode.insertBefore(n, t);
33308             }else if(p == "below"){
33309                 t.parentNode.insertBefore(n, t.nextSibling);
33310             }else{
33311                 t.appendChild(n);
33312             }
33313         }
33314         n.ui.focus();
33315         if(this.tree.hlDrop){
33316             n.ui.highlight();
33317         }
33318         t.ui.endDrop();
33319         this.tree.fireEvent("nodedrop", de);
33320     },
33321     
33322     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33323         if(this.tree.hlDrop){
33324             dropNode.ui.focus();
33325             dropNode.ui.highlight();
33326         }
33327         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33328     },
33329     
33330     getTree : function(){
33331         return this.tree;
33332     },
33333     
33334     removeDropIndicators : function(n){
33335         if(n && n.ddel){
33336             var el = n.ddel;
33337             Roo.fly(el).removeClass([
33338                     "x-tree-drag-insert-above",
33339                     "x-tree-drag-insert-below",
33340                     "x-tree-drag-append"]);
33341             this.lastInsertClass = "_noclass";
33342         }
33343     },
33344     
33345     beforeDragDrop : function(target, e, id){
33346         this.cancelExpand();
33347         return true;
33348     },
33349     
33350     afterRepair : function(data){
33351         if(data && Roo.enableFx){
33352             data.node.ui.highlight();
33353         }
33354         this.hideProxy();
33355     }    
33356 });
33357
33358 }
33359 /*
33360  * Based on:
33361  * Ext JS Library 1.1.1
33362  * Copyright(c) 2006-2007, Ext JS, LLC.
33363  *
33364  * Originally Released Under LGPL - original licence link has changed is not relivant.
33365  *
33366  * Fork - LGPL
33367  * <script type="text/javascript">
33368  */
33369  
33370
33371 if(Roo.dd.DragZone){
33372 Roo.tree.TreeDragZone = function(tree, config){
33373     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33374     this.tree = tree;
33375 };
33376
33377 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33378     ddGroup : "TreeDD",
33379     
33380     onBeforeDrag : function(data, e){
33381         var n = data.node;
33382         return n && n.draggable && !n.disabled;
33383     },
33384     
33385     onInitDrag : function(e){
33386         var data = this.dragData;
33387         this.tree.getSelectionModel().select(data.node);
33388         this.proxy.update("");
33389         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33390         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33391     },
33392     
33393     getRepairXY : function(e, data){
33394         return data.node.ui.getDDRepairXY();
33395     },
33396     
33397     onEndDrag : function(data, e){
33398         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33399     },
33400     
33401     onValidDrop : function(dd, e, id){
33402         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33403         this.hideProxy();
33404     },
33405     
33406     beforeInvalidDrop : function(e, id){
33407         // this scrolls the original position back into view
33408         var sm = this.tree.getSelectionModel();
33409         sm.clearSelections();
33410         sm.select(this.dragData.node);
33411     }
33412 });
33413 }/*
33414  * Based on:
33415  * Ext JS Library 1.1.1
33416  * Copyright(c) 2006-2007, Ext JS, LLC.
33417  *
33418  * Originally Released Under LGPL - original licence link has changed is not relivant.
33419  *
33420  * Fork - LGPL
33421  * <script type="text/javascript">
33422  */
33423 /**
33424  * @class Roo.tree.TreeEditor
33425  * @extends Roo.Editor
33426  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33427  * as the editor field.
33428  * @constructor
33429  * @param {TreePanel} tree
33430  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33431  */
33432 Roo.tree.TreeEditor = function(tree, config){
33433     config = config || {};
33434     var field = config.events ? config : new Roo.form.TextField(config);
33435     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33436
33437     this.tree = tree;
33438
33439     tree.on('beforeclick', this.beforeNodeClick, this);
33440     tree.getTreeEl().on('mousedown', this.hide, this);
33441     this.on('complete', this.updateNode, this);
33442     this.on('beforestartedit', this.fitToTree, this);
33443     this.on('startedit', this.bindScroll, this, {delay:10});
33444     this.on('specialkey', this.onSpecialKey, this);
33445 };
33446
33447 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33448     /**
33449      * @cfg {String} alignment
33450      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33451      */
33452     alignment: "l-l",
33453     // inherit
33454     autoSize: false,
33455     /**
33456      * @cfg {Boolean} hideEl
33457      * True to hide the bound element while the editor is displayed (defaults to false)
33458      */
33459     hideEl : false,
33460     /**
33461      * @cfg {String} cls
33462      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33463      */
33464     cls: "x-small-editor x-tree-editor",
33465     /**
33466      * @cfg {Boolean} shim
33467      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33468      */
33469     shim:false,
33470     // inherit
33471     shadow:"frame",
33472     /**
33473      * @cfg {Number} maxWidth
33474      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33475      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33476      * scroll and client offsets into account prior to each edit.
33477      */
33478     maxWidth: 250,
33479
33480     editDelay : 350,
33481
33482     // private
33483     fitToTree : function(ed, el){
33484         var td = this.tree.getTreeEl().dom, nd = el.dom;
33485         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33486             td.scrollLeft = nd.offsetLeft;
33487         }
33488         var w = Math.min(
33489                 this.maxWidth,
33490                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33491         this.setSize(w, '');
33492     },
33493
33494     // private
33495     triggerEdit : function(node){
33496         this.completeEdit();
33497         this.editNode = node;
33498         this.startEdit(node.ui.textNode, node.text);
33499     },
33500
33501     // private
33502     bindScroll : function(){
33503         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33504     },
33505
33506     // private
33507     beforeNodeClick : function(node, e){
33508         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33509         this.lastClick = new Date();
33510         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33511             e.stopEvent();
33512             this.triggerEdit(node);
33513             return false;
33514         }
33515     },
33516
33517     // private
33518     updateNode : function(ed, value){
33519         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33520         this.editNode.setText(value);
33521     },
33522
33523     // private
33524     onHide : function(){
33525         Roo.tree.TreeEditor.superclass.onHide.call(this);
33526         if(this.editNode){
33527             this.editNode.ui.focus();
33528         }
33529     },
33530
33531     // private
33532     onSpecialKey : function(field, e){
33533         var k = e.getKey();
33534         if(k == e.ESC){
33535             e.stopEvent();
33536             this.cancelEdit();
33537         }else if(k == e.ENTER && !e.hasModifier()){
33538             e.stopEvent();
33539             this.completeEdit();
33540         }
33541     }
33542 });//<Script type="text/javascript">
33543 /*
33544  * Based on:
33545  * Ext JS Library 1.1.1
33546  * Copyright(c) 2006-2007, Ext JS, LLC.
33547  *
33548  * Originally Released Under LGPL - original licence link has changed is not relivant.
33549  *
33550  * Fork - LGPL
33551  * <script type="text/javascript">
33552  */
33553  
33554 /**
33555  * Not documented??? - probably should be...
33556  */
33557
33558 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33559     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33560     
33561     renderElements : function(n, a, targetNode, bulkRender){
33562         //consel.log("renderElements?");
33563         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33564
33565         var t = n.getOwnerTree();
33566         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33567         
33568         var cols = t.columns;
33569         var bw = t.borderWidth;
33570         var c = cols[0];
33571         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33572          var cb = typeof a.checked == "boolean";
33573         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33574         var colcls = 'x-t-' + tid + '-c0';
33575         var buf = [
33576             '<li class="x-tree-node">',
33577             
33578                 
33579                 '<div class="x-tree-node-el ', a.cls,'">',
33580                     // extran...
33581                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33582                 
33583                 
33584                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33585                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33586                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33587                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33588                            (a.iconCls ? ' '+a.iconCls : ''),
33589                            '" unselectable="on" />',
33590                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33591                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33592                              
33593                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33594                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33595                             '<span unselectable="on" qtip="' + tx + '">',
33596                              tx,
33597                              '</span></a>' ,
33598                     '</div>',
33599                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33600                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33601                  ];
33602         for(var i = 1, len = cols.length; i < len; i++){
33603             c = cols[i];
33604             colcls = 'x-t-' + tid + '-c' +i;
33605             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33606             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33607                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33608                       "</div>");
33609          }
33610          
33611          buf.push(
33612             '</a>',
33613             '<div class="x-clear"></div></div>',
33614             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33615             "</li>");
33616         
33617         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33618             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33619                                 n.nextSibling.ui.getEl(), buf.join(""));
33620         }else{
33621             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33622         }
33623         var el = this.wrap.firstChild;
33624         this.elRow = el;
33625         this.elNode = el.firstChild;
33626         this.ranchor = el.childNodes[1];
33627         this.ctNode = this.wrap.childNodes[1];
33628         var cs = el.firstChild.childNodes;
33629         this.indentNode = cs[0];
33630         this.ecNode = cs[1];
33631         this.iconNode = cs[2];
33632         var index = 3;
33633         if(cb){
33634             this.checkbox = cs[3];
33635             index++;
33636         }
33637         this.anchor = cs[index];
33638         
33639         this.textNode = cs[index].firstChild;
33640         
33641         //el.on("click", this.onClick, this);
33642         //el.on("dblclick", this.onDblClick, this);
33643         
33644         
33645        // console.log(this);
33646     },
33647     initEvents : function(){
33648         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33649         
33650             
33651         var a = this.ranchor;
33652
33653         var el = Roo.get(a);
33654
33655         if(Roo.isOpera){ // opera render bug ignores the CSS
33656             el.setStyle("text-decoration", "none");
33657         }
33658
33659         el.on("click", this.onClick, this);
33660         el.on("dblclick", this.onDblClick, this);
33661         el.on("contextmenu", this.onContextMenu, this);
33662         
33663     },
33664     
33665     /*onSelectedChange : function(state){
33666         if(state){
33667             this.focus();
33668             this.addClass("x-tree-selected");
33669         }else{
33670             //this.blur();
33671             this.removeClass("x-tree-selected");
33672         }
33673     },*/
33674     addClass : function(cls){
33675         if(this.elRow){
33676             Roo.fly(this.elRow).addClass(cls);
33677         }
33678         
33679     },
33680     
33681     
33682     removeClass : function(cls){
33683         if(this.elRow){
33684             Roo.fly(this.elRow).removeClass(cls);
33685         }
33686     }
33687
33688     
33689     
33690 });//<Script type="text/javascript">
33691
33692 /*
33693  * Based on:
33694  * Ext JS Library 1.1.1
33695  * Copyright(c) 2006-2007, Ext JS, LLC.
33696  *
33697  * Originally Released Under LGPL - original licence link has changed is not relivant.
33698  *
33699  * Fork - LGPL
33700  * <script type="text/javascript">
33701  */
33702  
33703
33704 /**
33705  * @class Roo.tree.ColumnTree
33706  * @extends Roo.data.TreePanel
33707  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33708  * @cfg {int} borderWidth  compined right/left border allowance
33709  * @constructor
33710  * @param {String/HTMLElement/Element} el The container element
33711  * @param {Object} config
33712  */
33713 Roo.tree.ColumnTree =  function(el, config)
33714 {
33715    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33716    this.addEvents({
33717         /**
33718         * @event resize
33719         * Fire this event on a container when it resizes
33720         * @param {int} w Width
33721         * @param {int} h Height
33722         */
33723        "resize" : true
33724     });
33725     this.on('resize', this.onResize, this);
33726 };
33727
33728 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33729     //lines:false,
33730     
33731     
33732     borderWidth: Roo.isBorderBox ? 0 : 2, 
33733     headEls : false,
33734     
33735     render : function(){
33736         // add the header.....
33737        
33738         Roo.tree.ColumnTree.superclass.render.apply(this);
33739         
33740         this.el.addClass('x-column-tree');
33741         
33742         this.headers = this.el.createChild(
33743             {cls:'x-tree-headers'},this.innerCt.dom);
33744    
33745         var cols = this.columns, c;
33746         var totalWidth = 0;
33747         this.headEls = [];
33748         var  len = cols.length;
33749         for(var i = 0; i < len; i++){
33750              c = cols[i];
33751              totalWidth += c.width;
33752             this.headEls.push(this.headers.createChild({
33753                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33754                  cn: {
33755                      cls:'x-tree-hd-text',
33756                      html: c.header
33757                  },
33758                  style:'width:'+(c.width-this.borderWidth)+'px;'
33759              }));
33760         }
33761         this.headers.createChild({cls:'x-clear'});
33762         // prevent floats from wrapping when clipped
33763         this.headers.setWidth(totalWidth);
33764         //this.innerCt.setWidth(totalWidth);
33765         this.innerCt.setStyle({ overflow: 'auto' });
33766         this.onResize(this.width, this.height);
33767              
33768         
33769     },
33770     onResize : function(w,h)
33771     {
33772         this.height = h;
33773         this.width = w;
33774         // resize cols..
33775         this.innerCt.setWidth(this.width);
33776         this.innerCt.setHeight(this.height-20);
33777         
33778         // headers...
33779         var cols = this.columns, c;
33780         var totalWidth = 0;
33781         var expEl = false;
33782         var len = cols.length;
33783         for(var i = 0; i < len; i++){
33784             c = cols[i];
33785             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33786                 // it's the expander..
33787                 expEl  = this.headEls[i];
33788                 continue;
33789             }
33790             totalWidth += c.width;
33791             
33792         }
33793         if (expEl) {
33794             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33795         }
33796         this.headers.setWidth(w-20);
33797
33798         
33799         
33800         
33801     }
33802 });
33803 /*
33804  * Based on:
33805  * Ext JS Library 1.1.1
33806  * Copyright(c) 2006-2007, Ext JS, LLC.
33807  *
33808  * Originally Released Under LGPL - original licence link has changed is not relivant.
33809  *
33810  * Fork - LGPL
33811  * <script type="text/javascript">
33812  */
33813  
33814 /**
33815  * @class Roo.menu.Menu
33816  * @extends Roo.util.Observable
33817  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33818  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33819  * @constructor
33820  * Creates a new Menu
33821  * @param {Object} config Configuration options
33822  */
33823 Roo.menu.Menu = function(config){
33824     Roo.apply(this, config);
33825     this.id = this.id || Roo.id();
33826     this.addEvents({
33827         /**
33828          * @event beforeshow
33829          * Fires before this menu is displayed
33830          * @param {Roo.menu.Menu} this
33831          */
33832         beforeshow : true,
33833         /**
33834          * @event beforehide
33835          * Fires before this menu is hidden
33836          * @param {Roo.menu.Menu} this
33837          */
33838         beforehide : true,
33839         /**
33840          * @event show
33841          * Fires after this menu is displayed
33842          * @param {Roo.menu.Menu} this
33843          */
33844         show : true,
33845         /**
33846          * @event hide
33847          * Fires after this menu is hidden
33848          * @param {Roo.menu.Menu} this
33849          */
33850         hide : true,
33851         /**
33852          * @event click
33853          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33854          * @param {Roo.menu.Menu} this
33855          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33856          * @param {Roo.EventObject} e
33857          */
33858         click : true,
33859         /**
33860          * @event mouseover
33861          * Fires when the mouse is hovering over this menu
33862          * @param {Roo.menu.Menu} this
33863          * @param {Roo.EventObject} e
33864          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33865          */
33866         mouseover : true,
33867         /**
33868          * @event mouseout
33869          * Fires when the mouse exits this menu
33870          * @param {Roo.menu.Menu} this
33871          * @param {Roo.EventObject} e
33872          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33873          */
33874         mouseout : true,
33875         /**
33876          * @event itemclick
33877          * Fires when a menu item contained in this menu is clicked
33878          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33879          * @param {Roo.EventObject} e
33880          */
33881         itemclick: true
33882     });
33883     if (this.registerMenu) {
33884         Roo.menu.MenuMgr.register(this);
33885     }
33886     
33887     var mis = this.items;
33888     this.items = new Roo.util.MixedCollection();
33889     if(mis){
33890         this.add.apply(this, mis);
33891     }
33892 };
33893
33894 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33895     /**
33896      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33897      */
33898     minWidth : 120,
33899     /**
33900      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33901      * for bottom-right shadow (defaults to "sides")
33902      */
33903     shadow : "sides",
33904     /**
33905      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33906      * this menu (defaults to "tl-tr?")
33907      */
33908     subMenuAlign : "tl-tr?",
33909     /**
33910      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33911      * relative to its element of origin (defaults to "tl-bl?")
33912      */
33913     defaultAlign : "tl-bl?",
33914     /**
33915      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33916      */
33917     allowOtherMenus : false,
33918     /**
33919      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33920      */
33921     registerMenu : true,
33922
33923     hidden:true,
33924
33925     // private
33926     render : function(){
33927         if(this.el){
33928             return;
33929         }
33930         var el = this.el = new Roo.Layer({
33931             cls: "x-menu",
33932             shadow:this.shadow,
33933             constrain: false,
33934             parentEl: this.parentEl || document.body,
33935             zindex:15000
33936         });
33937
33938         this.keyNav = new Roo.menu.MenuNav(this);
33939
33940         if(this.plain){
33941             el.addClass("x-menu-plain");
33942         }
33943         if(this.cls){
33944             el.addClass(this.cls);
33945         }
33946         // generic focus element
33947         this.focusEl = el.createChild({
33948             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33949         });
33950         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33951         ul.on("click", this.onClick, this);
33952         ul.on("mouseover", this.onMouseOver, this);
33953         ul.on("mouseout", this.onMouseOut, this);
33954         this.items.each(function(item){
33955             var li = document.createElement("li");
33956             li.className = "x-menu-list-item";
33957             ul.dom.appendChild(li);
33958             item.render(li, this);
33959         }, this);
33960         this.ul = ul;
33961         this.autoWidth();
33962     },
33963
33964     // private
33965     autoWidth : function(){
33966         var el = this.el, ul = this.ul;
33967         if(!el){
33968             return;
33969         }
33970         var w = this.width;
33971         if(w){
33972             el.setWidth(w);
33973         }else if(Roo.isIE){
33974             el.setWidth(this.minWidth);
33975             var t = el.dom.offsetWidth; // force recalc
33976             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33977         }
33978     },
33979
33980     // private
33981     delayAutoWidth : function(){
33982         if(this.rendered){
33983             if(!this.awTask){
33984                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33985             }
33986             this.awTask.delay(20);
33987         }
33988     },
33989
33990     // private
33991     findTargetItem : function(e){
33992         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33993         if(t && t.menuItemId){
33994             return this.items.get(t.menuItemId);
33995         }
33996     },
33997
33998     // private
33999     onClick : function(e){
34000         var t;
34001         if(t = this.findTargetItem(e)){
34002             t.onClick(e);
34003             this.fireEvent("click", this, t, e);
34004         }
34005     },
34006
34007     // private
34008     setActiveItem : function(item, autoExpand){
34009         if(item != this.activeItem){
34010             if(this.activeItem){
34011                 this.activeItem.deactivate();
34012             }
34013             this.activeItem = item;
34014             item.activate(autoExpand);
34015         }else if(autoExpand){
34016             item.expandMenu();
34017         }
34018     },
34019
34020     // private
34021     tryActivate : function(start, step){
34022         var items = this.items;
34023         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34024             var item = items.get(i);
34025             if(!item.disabled && item.canActivate){
34026                 this.setActiveItem(item, false);
34027                 return item;
34028             }
34029         }
34030         return false;
34031     },
34032
34033     // private
34034     onMouseOver : function(e){
34035         var t;
34036         if(t = this.findTargetItem(e)){
34037             if(t.canActivate && !t.disabled){
34038                 this.setActiveItem(t, true);
34039             }
34040         }
34041         this.fireEvent("mouseover", this, e, t);
34042     },
34043
34044     // private
34045     onMouseOut : function(e){
34046         var t;
34047         if(t = this.findTargetItem(e)){
34048             if(t == this.activeItem && t.shouldDeactivate(e)){
34049                 this.activeItem.deactivate();
34050                 delete this.activeItem;
34051             }
34052         }
34053         this.fireEvent("mouseout", this, e, t);
34054     },
34055
34056     /**
34057      * Read-only.  Returns true if the menu is currently displayed, else false.
34058      * @type Boolean
34059      */
34060     isVisible : function(){
34061         return this.el && !this.hidden;
34062     },
34063
34064     /**
34065      * Displays this menu relative to another element
34066      * @param {String/HTMLElement/Roo.Element} element The element to align to
34067      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34068      * the element (defaults to this.defaultAlign)
34069      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34070      */
34071     show : function(el, pos, parentMenu){
34072         this.parentMenu = parentMenu;
34073         if(!this.el){
34074             this.render();
34075         }
34076         this.fireEvent("beforeshow", this);
34077         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34078     },
34079
34080     /**
34081      * Displays this menu at a specific xy position
34082      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34083      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34084      */
34085     showAt : function(xy, parentMenu, /* private: */_e){
34086         this.parentMenu = parentMenu;
34087         if(!this.el){
34088             this.render();
34089         }
34090         if(_e !== false){
34091             this.fireEvent("beforeshow", this);
34092             xy = this.el.adjustForConstraints(xy);
34093         }
34094         this.el.setXY(xy);
34095         this.el.show();
34096         this.hidden = false;
34097         this.focus();
34098         this.fireEvent("show", this);
34099     },
34100
34101     focus : function(){
34102         if(!this.hidden){
34103             this.doFocus.defer(50, this);
34104         }
34105     },
34106
34107     doFocus : function(){
34108         if(!this.hidden){
34109             this.focusEl.focus();
34110         }
34111     },
34112
34113     /**
34114      * Hides this menu and optionally all parent menus
34115      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34116      */
34117     hide : function(deep){
34118         if(this.el && this.isVisible()){
34119             this.fireEvent("beforehide", this);
34120             if(this.activeItem){
34121                 this.activeItem.deactivate();
34122                 this.activeItem = null;
34123             }
34124             this.el.hide();
34125             this.hidden = true;
34126             this.fireEvent("hide", this);
34127         }
34128         if(deep === true && this.parentMenu){
34129             this.parentMenu.hide(true);
34130         }
34131     },
34132
34133     /**
34134      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34135      * Any of the following are valid:
34136      * <ul>
34137      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34138      * <li>An HTMLElement object which will be converted to a menu item</li>
34139      * <li>A menu item config object that will be created as a new menu item</li>
34140      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34141      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34142      * </ul>
34143      * Usage:
34144      * <pre><code>
34145 // Create the menu
34146 var menu = new Roo.menu.Menu();
34147
34148 // Create a menu item to add by reference
34149 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34150
34151 // Add a bunch of items at once using different methods.
34152 // Only the last item added will be returned.
34153 var item = menu.add(
34154     menuItem,                // add existing item by ref
34155     'Dynamic Item',          // new TextItem
34156     '-',                     // new separator
34157     { text: 'Config Item' }  // new item by config
34158 );
34159 </code></pre>
34160      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34161      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34162      */
34163     add : function(){
34164         var a = arguments, l = a.length, item;
34165         for(var i = 0; i < l; i++){
34166             var el = a[i];
34167             if ((typeof(el) == "object") && el.xtype && el.xns) {
34168                 el = Roo.factory(el, Roo.menu);
34169             }
34170             
34171             if(el.render){ // some kind of Item
34172                 item = this.addItem(el);
34173             }else if(typeof el == "string"){ // string
34174                 if(el == "separator" || el == "-"){
34175                     item = this.addSeparator();
34176                 }else{
34177                     item = this.addText(el);
34178                 }
34179             }else if(el.tagName || el.el){ // element
34180                 item = this.addElement(el);
34181             }else if(typeof el == "object"){ // must be menu item config?
34182                 item = this.addMenuItem(el);
34183             }
34184         }
34185         return item;
34186     },
34187
34188     /**
34189      * Returns this menu's underlying {@link Roo.Element} object
34190      * @return {Roo.Element} The element
34191      */
34192     getEl : function(){
34193         if(!this.el){
34194             this.render();
34195         }
34196         return this.el;
34197     },
34198
34199     /**
34200      * Adds a separator bar to the menu
34201      * @return {Roo.menu.Item} The menu item that was added
34202      */
34203     addSeparator : function(){
34204         return this.addItem(new Roo.menu.Separator());
34205     },
34206
34207     /**
34208      * Adds an {@link Roo.Element} object to the menu
34209      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34210      * @return {Roo.menu.Item} The menu item that was added
34211      */
34212     addElement : function(el){
34213         return this.addItem(new Roo.menu.BaseItem(el));
34214     },
34215
34216     /**
34217      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34218      * @param {Roo.menu.Item} item The menu item to add
34219      * @return {Roo.menu.Item} The menu item that was added
34220      */
34221     addItem : function(item){
34222         this.items.add(item);
34223         if(this.ul){
34224             var li = document.createElement("li");
34225             li.className = "x-menu-list-item";
34226             this.ul.dom.appendChild(li);
34227             item.render(li, this);
34228             this.delayAutoWidth();
34229         }
34230         return item;
34231     },
34232
34233     /**
34234      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34235      * @param {Object} config A MenuItem config object
34236      * @return {Roo.menu.Item} The menu item that was added
34237      */
34238     addMenuItem : function(config){
34239         if(!(config instanceof Roo.menu.Item)){
34240             if(typeof config.checked == "boolean"){ // must be check menu item config?
34241                 config = new Roo.menu.CheckItem(config);
34242             }else{
34243                 config = new Roo.menu.Item(config);
34244             }
34245         }
34246         return this.addItem(config);
34247     },
34248
34249     /**
34250      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34251      * @param {String} text The text to display in the menu item
34252      * @return {Roo.menu.Item} The menu item that was added
34253      */
34254     addText : function(text){
34255         return this.addItem(new Roo.menu.TextItem({ text : text }));
34256     },
34257
34258     /**
34259      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34260      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34261      * @param {Roo.menu.Item} item The menu item to add
34262      * @return {Roo.menu.Item} The menu item that was added
34263      */
34264     insert : function(index, item){
34265         this.items.insert(index, item);
34266         if(this.ul){
34267             var li = document.createElement("li");
34268             li.className = "x-menu-list-item";
34269             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34270             item.render(li, this);
34271             this.delayAutoWidth();
34272         }
34273         return item;
34274     },
34275
34276     /**
34277      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34278      * @param {Roo.menu.Item} item The menu item to remove
34279      */
34280     remove : function(item){
34281         this.items.removeKey(item.id);
34282         item.destroy();
34283     },
34284
34285     /**
34286      * Removes and destroys all items in the menu
34287      */
34288     removeAll : function(){
34289         var f;
34290         while(f = this.items.first()){
34291             this.remove(f);
34292         }
34293     }
34294 });
34295
34296 // MenuNav is a private utility class used internally by the Menu
34297 Roo.menu.MenuNav = function(menu){
34298     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34299     this.scope = this.menu = menu;
34300 };
34301
34302 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34303     doRelay : function(e, h){
34304         var k = e.getKey();
34305         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34306             this.menu.tryActivate(0, 1);
34307             return false;
34308         }
34309         return h.call(this.scope || this, e, this.menu);
34310     },
34311
34312     up : function(e, m){
34313         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34314             m.tryActivate(m.items.length-1, -1);
34315         }
34316     },
34317
34318     down : function(e, m){
34319         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34320             m.tryActivate(0, 1);
34321         }
34322     },
34323
34324     right : function(e, m){
34325         if(m.activeItem){
34326             m.activeItem.expandMenu(true);
34327         }
34328     },
34329
34330     left : function(e, m){
34331         m.hide();
34332         if(m.parentMenu && m.parentMenu.activeItem){
34333             m.parentMenu.activeItem.activate();
34334         }
34335     },
34336
34337     enter : function(e, m){
34338         if(m.activeItem){
34339             e.stopPropagation();
34340             m.activeItem.onClick(e);
34341             m.fireEvent("click", this, m.activeItem);
34342             return true;
34343         }
34344     }
34345 });/*
34346  * Based on:
34347  * Ext JS Library 1.1.1
34348  * Copyright(c) 2006-2007, Ext JS, LLC.
34349  *
34350  * Originally Released Under LGPL - original licence link has changed is not relivant.
34351  *
34352  * Fork - LGPL
34353  * <script type="text/javascript">
34354  */
34355  
34356 /**
34357  * @class Roo.menu.MenuMgr
34358  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34359  * @singleton
34360  */
34361 Roo.menu.MenuMgr = function(){
34362    var menus, active, groups = {}, attached = false, lastShow = new Date();
34363
34364    // private - called when first menu is created
34365    function init(){
34366        menus = {};
34367        active = new Roo.util.MixedCollection();
34368        Roo.get(document).addKeyListener(27, function(){
34369            if(active.length > 0){
34370                hideAll();
34371            }
34372        });
34373    }
34374
34375    // private
34376    function hideAll(){
34377        if(active && active.length > 0){
34378            var c = active.clone();
34379            c.each(function(m){
34380                m.hide();
34381            });
34382        }
34383    }
34384
34385    // private
34386    function onHide(m){
34387        active.remove(m);
34388        if(active.length < 1){
34389            Roo.get(document).un("mousedown", onMouseDown);
34390            attached = false;
34391        }
34392    }
34393
34394    // private
34395    function onShow(m){
34396        var last = active.last();
34397        lastShow = new Date();
34398        active.add(m);
34399        if(!attached){
34400            Roo.get(document).on("mousedown", onMouseDown);
34401            attached = true;
34402        }
34403        if(m.parentMenu){
34404           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34405           m.parentMenu.activeChild = m;
34406        }else if(last && last.isVisible()){
34407           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34408        }
34409    }
34410
34411    // private
34412    function onBeforeHide(m){
34413        if(m.activeChild){
34414            m.activeChild.hide();
34415        }
34416        if(m.autoHideTimer){
34417            clearTimeout(m.autoHideTimer);
34418            delete m.autoHideTimer;
34419        }
34420    }
34421
34422    // private
34423    function onBeforeShow(m){
34424        var pm = m.parentMenu;
34425        if(!pm && !m.allowOtherMenus){
34426            hideAll();
34427        }else if(pm && pm.activeChild && active != m){
34428            pm.activeChild.hide();
34429        }
34430    }
34431
34432    // private
34433    function onMouseDown(e){
34434        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34435            hideAll();
34436        }
34437    }
34438
34439    // private
34440    function onBeforeCheck(mi, state){
34441        if(state){
34442            var g = groups[mi.group];
34443            for(var i = 0, l = g.length; i < l; i++){
34444                if(g[i] != mi){
34445                    g[i].setChecked(false);
34446                }
34447            }
34448        }
34449    }
34450
34451    return {
34452
34453        /**
34454         * Hides all menus that are currently visible
34455         */
34456        hideAll : function(){
34457             hideAll();  
34458        },
34459
34460        // private
34461        register : function(menu){
34462            if(!menus){
34463                init();
34464            }
34465            menus[menu.id] = menu;
34466            menu.on("beforehide", onBeforeHide);
34467            menu.on("hide", onHide);
34468            menu.on("beforeshow", onBeforeShow);
34469            menu.on("show", onShow);
34470            var g = menu.group;
34471            if(g && menu.events["checkchange"]){
34472                if(!groups[g]){
34473                    groups[g] = [];
34474                }
34475                groups[g].push(menu);
34476                menu.on("checkchange", onCheck);
34477            }
34478        },
34479
34480         /**
34481          * Returns a {@link Roo.menu.Menu} object
34482          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34483          * be used to generate and return a new Menu instance.
34484          */
34485        get : function(menu){
34486            if(typeof menu == "string"){ // menu id
34487                return menus[menu];
34488            }else if(menu.events){  // menu instance
34489                return menu;
34490            }else if(typeof menu.length == 'number'){ // array of menu items?
34491                return new Roo.menu.Menu({items:menu});
34492            }else{ // otherwise, must be a config
34493                return new Roo.menu.Menu(menu);
34494            }
34495        },
34496
34497        // private
34498        unregister : function(menu){
34499            delete menus[menu.id];
34500            menu.un("beforehide", onBeforeHide);
34501            menu.un("hide", onHide);
34502            menu.un("beforeshow", onBeforeShow);
34503            menu.un("show", onShow);
34504            var g = menu.group;
34505            if(g && menu.events["checkchange"]){
34506                groups[g].remove(menu);
34507                menu.un("checkchange", onCheck);
34508            }
34509        },
34510
34511        // private
34512        registerCheckable : function(menuItem){
34513            var g = menuItem.group;
34514            if(g){
34515                if(!groups[g]){
34516                    groups[g] = [];
34517                }
34518                groups[g].push(menuItem);
34519                menuItem.on("beforecheckchange", onBeforeCheck);
34520            }
34521        },
34522
34523        // private
34524        unregisterCheckable : function(menuItem){
34525            var g = menuItem.group;
34526            if(g){
34527                groups[g].remove(menuItem);
34528                menuItem.un("beforecheckchange", onBeforeCheck);
34529            }
34530        }
34531    };
34532 }();/*
34533  * Based on:
34534  * Ext JS Library 1.1.1
34535  * Copyright(c) 2006-2007, Ext JS, LLC.
34536  *
34537  * Originally Released Under LGPL - original licence link has changed is not relivant.
34538  *
34539  * Fork - LGPL
34540  * <script type="text/javascript">
34541  */
34542  
34543
34544 /**
34545  * @class Roo.menu.BaseItem
34546  * @extends Roo.Component
34547  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34548  * management and base configuration options shared by all menu components.
34549  * @constructor
34550  * Creates a new BaseItem
34551  * @param {Object} config Configuration options
34552  */
34553 Roo.menu.BaseItem = function(config){
34554     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34555
34556     this.addEvents({
34557         /**
34558          * @event click
34559          * Fires when this item is clicked
34560          * @param {Roo.menu.BaseItem} this
34561          * @param {Roo.EventObject} e
34562          */
34563         click: true,
34564         /**
34565          * @event activate
34566          * Fires when this item is activated
34567          * @param {Roo.menu.BaseItem} this
34568          */
34569         activate : true,
34570         /**
34571          * @event deactivate
34572          * Fires when this item is deactivated
34573          * @param {Roo.menu.BaseItem} this
34574          */
34575         deactivate : true
34576     });
34577
34578     if(this.handler){
34579         this.on("click", this.handler, this.scope, true);
34580     }
34581 };
34582
34583 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34584     /**
34585      * @cfg {Function} handler
34586      * A function that will handle the click event of this menu item (defaults to undefined)
34587      */
34588     /**
34589      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34590      */
34591     canActivate : false,
34592     /**
34593      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34594      */
34595     activeClass : "x-menu-item-active",
34596     /**
34597      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34598      */
34599     hideOnClick : true,
34600     /**
34601      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34602      */
34603     hideDelay : 100,
34604
34605     // private
34606     ctype: "Roo.menu.BaseItem",
34607
34608     // private
34609     actionMode : "container",
34610
34611     // private
34612     render : function(container, parentMenu){
34613         this.parentMenu = parentMenu;
34614         Roo.menu.BaseItem.superclass.render.call(this, container);
34615         this.container.menuItemId = this.id;
34616     },
34617
34618     // private
34619     onRender : function(container, position){
34620         this.el = Roo.get(this.el);
34621         container.dom.appendChild(this.el.dom);
34622     },
34623
34624     // private
34625     onClick : function(e){
34626         if(!this.disabled && this.fireEvent("click", this, e) !== false
34627                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34628             this.handleClick(e);
34629         }else{
34630             e.stopEvent();
34631         }
34632     },
34633
34634     // private
34635     activate : function(){
34636         if(this.disabled){
34637             return false;
34638         }
34639         var li = this.container;
34640         li.addClass(this.activeClass);
34641         this.region = li.getRegion().adjust(2, 2, -2, -2);
34642         this.fireEvent("activate", this);
34643         return true;
34644     },
34645
34646     // private
34647     deactivate : function(){
34648         this.container.removeClass(this.activeClass);
34649         this.fireEvent("deactivate", this);
34650     },
34651
34652     // private
34653     shouldDeactivate : function(e){
34654         return !this.region || !this.region.contains(e.getPoint());
34655     },
34656
34657     // private
34658     handleClick : function(e){
34659         if(this.hideOnClick){
34660             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34661         }
34662     },
34663
34664     // private
34665     expandMenu : function(autoActivate){
34666         // do nothing
34667     },
34668
34669     // private
34670     hideMenu : function(){
34671         // do nothing
34672     }
34673 });/*
34674  * Based on:
34675  * Ext JS Library 1.1.1
34676  * Copyright(c) 2006-2007, Ext JS, LLC.
34677  *
34678  * Originally Released Under LGPL - original licence link has changed is not relivant.
34679  *
34680  * Fork - LGPL
34681  * <script type="text/javascript">
34682  */
34683  
34684 /**
34685  * @class Roo.menu.Adapter
34686  * @extends Roo.menu.BaseItem
34687  * 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.
34688  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34689  * @constructor
34690  * Creates a new Adapter
34691  * @param {Object} config Configuration options
34692  */
34693 Roo.menu.Adapter = function(component, config){
34694     Roo.menu.Adapter.superclass.constructor.call(this, config);
34695     this.component = component;
34696 };
34697 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34698     // private
34699     canActivate : true,
34700
34701     // private
34702     onRender : function(container, position){
34703         this.component.render(container);
34704         this.el = this.component.getEl();
34705     },
34706
34707     // private
34708     activate : function(){
34709         if(this.disabled){
34710             return false;
34711         }
34712         this.component.focus();
34713         this.fireEvent("activate", this);
34714         return true;
34715     },
34716
34717     // private
34718     deactivate : function(){
34719         this.fireEvent("deactivate", this);
34720     },
34721
34722     // private
34723     disable : function(){
34724         this.component.disable();
34725         Roo.menu.Adapter.superclass.disable.call(this);
34726     },
34727
34728     // private
34729     enable : function(){
34730         this.component.enable();
34731         Roo.menu.Adapter.superclass.enable.call(this);
34732     }
34733 });/*
34734  * Based on:
34735  * Ext JS Library 1.1.1
34736  * Copyright(c) 2006-2007, Ext JS, LLC.
34737  *
34738  * Originally Released Under LGPL - original licence link has changed is not relivant.
34739  *
34740  * Fork - LGPL
34741  * <script type="text/javascript">
34742  */
34743
34744 /**
34745  * @class Roo.menu.TextItem
34746  * @extends Roo.menu.BaseItem
34747  * Adds a static text string to a menu, usually used as either a heading or group separator.
34748  * Note: old style constructor with text is still supported.
34749  * 
34750  * @constructor
34751  * Creates a new TextItem
34752  * @param {Object} cfg Configuration
34753  */
34754 Roo.menu.TextItem = function(cfg){
34755     if (typeof(cfg) == 'string') {
34756         this.text = cfg;
34757     } else {
34758         Roo.apply(this,cfg);
34759     }
34760     
34761     Roo.menu.TextItem.superclass.constructor.call(this);
34762 };
34763
34764 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34765     /**
34766      * @cfg {Boolean} text Text to show on item.
34767      */
34768     text : '',
34769     
34770     /**
34771      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34772      */
34773     hideOnClick : false,
34774     /**
34775      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34776      */
34777     itemCls : "x-menu-text",
34778
34779     // private
34780     onRender : function(){
34781         var s = document.createElement("span");
34782         s.className = this.itemCls;
34783         s.innerHTML = this.text;
34784         this.el = s;
34785         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34786     }
34787 });/*
34788  * Based on:
34789  * Ext JS Library 1.1.1
34790  * Copyright(c) 2006-2007, Ext JS, LLC.
34791  *
34792  * Originally Released Under LGPL - original licence link has changed is not relivant.
34793  *
34794  * Fork - LGPL
34795  * <script type="text/javascript">
34796  */
34797
34798 /**
34799  * @class Roo.menu.Separator
34800  * @extends Roo.menu.BaseItem
34801  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34802  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34803  * @constructor
34804  * @param {Object} config Configuration options
34805  */
34806 Roo.menu.Separator = function(config){
34807     Roo.menu.Separator.superclass.constructor.call(this, config);
34808 };
34809
34810 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34811     /**
34812      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34813      */
34814     itemCls : "x-menu-sep",
34815     /**
34816      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34817      */
34818     hideOnClick : false,
34819
34820     // private
34821     onRender : function(li){
34822         var s = document.createElement("span");
34823         s.className = this.itemCls;
34824         s.innerHTML = "&#160;";
34825         this.el = s;
34826         li.addClass("x-menu-sep-li");
34827         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34828     }
34829 });/*
34830  * Based on:
34831  * Ext JS Library 1.1.1
34832  * Copyright(c) 2006-2007, Ext JS, LLC.
34833  *
34834  * Originally Released Under LGPL - original licence link has changed is not relivant.
34835  *
34836  * Fork - LGPL
34837  * <script type="text/javascript">
34838  */
34839 /**
34840  * @class Roo.menu.Item
34841  * @extends Roo.menu.BaseItem
34842  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34843  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34844  * activation and click handling.
34845  * @constructor
34846  * Creates a new Item
34847  * @param {Object} config Configuration options
34848  */
34849 Roo.menu.Item = function(config){
34850     Roo.menu.Item.superclass.constructor.call(this, config);
34851     if(this.menu){
34852         this.menu = Roo.menu.MenuMgr.get(this.menu);
34853     }
34854 };
34855 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34856     
34857     /**
34858      * @cfg {String} text
34859      * The text to show on the menu item.
34860      */
34861     text: '',
34862      /**
34863      * @cfg {String} HTML to render in menu
34864      * The text to show on the menu item (HTML version).
34865      */
34866     html: '',
34867     /**
34868      * @cfg {String} icon
34869      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34870      */
34871     icon: undefined,
34872     /**
34873      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34874      */
34875     itemCls : "x-menu-item",
34876     /**
34877      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34878      */
34879     canActivate : true,
34880     /**
34881      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34882      */
34883     showDelay: 200,
34884     // doc'd in BaseItem
34885     hideDelay: 200,
34886
34887     // private
34888     ctype: "Roo.menu.Item",
34889     
34890     // private
34891     onRender : function(container, position){
34892         var el = document.createElement("a");
34893         el.hideFocus = true;
34894         el.unselectable = "on";
34895         el.href = this.href || "#";
34896         if(this.hrefTarget){
34897             el.target = this.hrefTarget;
34898         }
34899         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34900         
34901         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34902         
34903         el.innerHTML = String.format(
34904                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34905                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34906         this.el = el;
34907         Roo.menu.Item.superclass.onRender.call(this, container, position);
34908     },
34909
34910     /**
34911      * Sets the text to display in this menu item
34912      * @param {String} text The text to display
34913      * @param {Boolean} isHTML true to indicate text is pure html.
34914      */
34915     setText : function(text, isHTML){
34916         if (isHTML) {
34917             this.html = text;
34918         } else {
34919             this.text = text;
34920             this.html = '';
34921         }
34922         if(this.rendered){
34923             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34924      
34925             this.el.update(String.format(
34926                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34927                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34928             this.parentMenu.autoWidth();
34929         }
34930     },
34931
34932     // private
34933     handleClick : function(e){
34934         if(!this.href){ // if no link defined, stop the event automatically
34935             e.stopEvent();
34936         }
34937         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34938     },
34939
34940     // private
34941     activate : function(autoExpand){
34942         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34943             this.focus();
34944             if(autoExpand){
34945                 this.expandMenu();
34946             }
34947         }
34948         return true;
34949     },
34950
34951     // private
34952     shouldDeactivate : function(e){
34953         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34954             if(this.menu && this.menu.isVisible()){
34955                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34956             }
34957             return true;
34958         }
34959         return false;
34960     },
34961
34962     // private
34963     deactivate : function(){
34964         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34965         this.hideMenu();
34966     },
34967
34968     // private
34969     expandMenu : function(autoActivate){
34970         if(!this.disabled && this.menu){
34971             clearTimeout(this.hideTimer);
34972             delete this.hideTimer;
34973             if(!this.menu.isVisible() && !this.showTimer){
34974                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34975             }else if (this.menu.isVisible() && autoActivate){
34976                 this.menu.tryActivate(0, 1);
34977             }
34978         }
34979     },
34980
34981     // private
34982     deferExpand : function(autoActivate){
34983         delete this.showTimer;
34984         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34985         if(autoActivate){
34986             this.menu.tryActivate(0, 1);
34987         }
34988     },
34989
34990     // private
34991     hideMenu : function(){
34992         clearTimeout(this.showTimer);
34993         delete this.showTimer;
34994         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34995             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34996         }
34997     },
34998
34999     // private
35000     deferHide : function(){
35001         delete this.hideTimer;
35002         this.menu.hide();
35003     }
35004 });/*
35005  * Based on:
35006  * Ext JS Library 1.1.1
35007  * Copyright(c) 2006-2007, Ext JS, LLC.
35008  *
35009  * Originally Released Under LGPL - original licence link has changed is not relivant.
35010  *
35011  * Fork - LGPL
35012  * <script type="text/javascript">
35013  */
35014  
35015 /**
35016  * @class Roo.menu.CheckItem
35017  * @extends Roo.menu.Item
35018  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35019  * @constructor
35020  * Creates a new CheckItem
35021  * @param {Object} config Configuration options
35022  */
35023 Roo.menu.CheckItem = function(config){
35024     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35025     this.addEvents({
35026         /**
35027          * @event beforecheckchange
35028          * Fires before the checked value is set, providing an opportunity to cancel if needed
35029          * @param {Roo.menu.CheckItem} this
35030          * @param {Boolean} checked The new checked value that will be set
35031          */
35032         "beforecheckchange" : true,
35033         /**
35034          * @event checkchange
35035          * Fires after the checked value has been set
35036          * @param {Roo.menu.CheckItem} this
35037          * @param {Boolean} checked The checked value that was set
35038          */
35039         "checkchange" : true
35040     });
35041     if(this.checkHandler){
35042         this.on('checkchange', this.checkHandler, this.scope);
35043     }
35044 };
35045 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35046     /**
35047      * @cfg {String} group
35048      * All check items with the same group name will automatically be grouped into a single-select
35049      * radio button group (defaults to '')
35050      */
35051     /**
35052      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35053      */
35054     itemCls : "x-menu-item x-menu-check-item",
35055     /**
35056      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35057      */
35058     groupClass : "x-menu-group-item",
35059
35060     /**
35061      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35062      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35063      * initialized with checked = true will be rendered as checked.
35064      */
35065     checked: false,
35066
35067     // private
35068     ctype: "Roo.menu.CheckItem",
35069
35070     // private
35071     onRender : function(c){
35072         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35073         if(this.group){
35074             this.el.addClass(this.groupClass);
35075         }
35076         Roo.menu.MenuMgr.registerCheckable(this);
35077         if(this.checked){
35078             this.checked = false;
35079             this.setChecked(true, true);
35080         }
35081     },
35082
35083     // private
35084     destroy : function(){
35085         if(this.rendered){
35086             Roo.menu.MenuMgr.unregisterCheckable(this);
35087         }
35088         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35089     },
35090
35091     /**
35092      * Set the checked state of this item
35093      * @param {Boolean} checked The new checked value
35094      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35095      */
35096     setChecked : function(state, suppressEvent){
35097         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35098             if(this.container){
35099                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35100             }
35101             this.checked = state;
35102             if(suppressEvent !== true){
35103                 this.fireEvent("checkchange", this, state);
35104             }
35105         }
35106     },
35107
35108     // private
35109     handleClick : function(e){
35110        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35111            this.setChecked(!this.checked);
35112        }
35113        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35114     }
35115 });/*
35116  * Based on:
35117  * Ext JS Library 1.1.1
35118  * Copyright(c) 2006-2007, Ext JS, LLC.
35119  *
35120  * Originally Released Under LGPL - original licence link has changed is not relivant.
35121  *
35122  * Fork - LGPL
35123  * <script type="text/javascript">
35124  */
35125  
35126 /**
35127  * @class Roo.menu.DateItem
35128  * @extends Roo.menu.Adapter
35129  * A menu item that wraps the {@link Roo.DatPicker} component.
35130  * @constructor
35131  * Creates a new DateItem
35132  * @param {Object} config Configuration options
35133  */
35134 Roo.menu.DateItem = function(config){
35135     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35136     /** The Roo.DatePicker object @type Roo.DatePicker */
35137     this.picker = this.component;
35138     this.addEvents({select: true});
35139     
35140     this.picker.on("render", function(picker){
35141         picker.getEl().swallowEvent("click");
35142         picker.container.addClass("x-menu-date-item");
35143     });
35144
35145     this.picker.on("select", this.onSelect, this);
35146 };
35147
35148 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35149     // private
35150     onSelect : function(picker, date){
35151         this.fireEvent("select", this, date, picker);
35152         Roo.menu.DateItem.superclass.handleClick.call(this);
35153     }
35154 });/*
35155  * Based on:
35156  * Ext JS Library 1.1.1
35157  * Copyright(c) 2006-2007, Ext JS, LLC.
35158  *
35159  * Originally Released Under LGPL - original licence link has changed is not relivant.
35160  *
35161  * Fork - LGPL
35162  * <script type="text/javascript">
35163  */
35164  
35165 /**
35166  * @class Roo.menu.ColorItem
35167  * @extends Roo.menu.Adapter
35168  * A menu item that wraps the {@link Roo.ColorPalette} component.
35169  * @constructor
35170  * Creates a new ColorItem
35171  * @param {Object} config Configuration options
35172  */
35173 Roo.menu.ColorItem = function(config){
35174     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35175     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35176     this.palette = this.component;
35177     this.relayEvents(this.palette, ["select"]);
35178     if(this.selectHandler){
35179         this.on('select', this.selectHandler, this.scope);
35180     }
35181 };
35182 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35183  * Based on:
35184  * Ext JS Library 1.1.1
35185  * Copyright(c) 2006-2007, Ext JS, LLC.
35186  *
35187  * Originally Released Under LGPL - original licence link has changed is not relivant.
35188  *
35189  * Fork - LGPL
35190  * <script type="text/javascript">
35191  */
35192  
35193
35194 /**
35195  * @class Roo.menu.DateMenu
35196  * @extends Roo.menu.Menu
35197  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35198  * @constructor
35199  * Creates a new DateMenu
35200  * @param {Object} config Configuration options
35201  */
35202 Roo.menu.DateMenu = function(config){
35203     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35204     this.plain = true;
35205     var di = new Roo.menu.DateItem(config);
35206     this.add(di);
35207     /**
35208      * The {@link Roo.DatePicker} instance for this DateMenu
35209      * @type DatePicker
35210      */
35211     this.picker = di.picker;
35212     /**
35213      * @event select
35214      * @param {DatePicker} picker
35215      * @param {Date} date
35216      */
35217     this.relayEvents(di, ["select"]);
35218
35219     this.on('beforeshow', function(){
35220         if(this.picker){
35221             this.picker.hideMonthPicker(true);
35222         }
35223     }, this);
35224 };
35225 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35226     cls:'x-date-menu'
35227 });/*
35228  * Based on:
35229  * Ext JS Library 1.1.1
35230  * Copyright(c) 2006-2007, Ext JS, LLC.
35231  *
35232  * Originally Released Under LGPL - original licence link has changed is not relivant.
35233  *
35234  * Fork - LGPL
35235  * <script type="text/javascript">
35236  */
35237  
35238
35239 /**
35240  * @class Roo.menu.ColorMenu
35241  * @extends Roo.menu.Menu
35242  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35243  * @constructor
35244  * Creates a new ColorMenu
35245  * @param {Object} config Configuration options
35246  */
35247 Roo.menu.ColorMenu = function(config){
35248     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35249     this.plain = true;
35250     var ci = new Roo.menu.ColorItem(config);
35251     this.add(ci);
35252     /**
35253      * The {@link Roo.ColorPalette} instance for this ColorMenu
35254      * @type ColorPalette
35255      */
35256     this.palette = ci.palette;
35257     /**
35258      * @event select
35259      * @param {ColorPalette} palette
35260      * @param {String} color
35261      */
35262     this.relayEvents(ci, ["select"]);
35263 };
35264 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35265  * Based on:
35266  * Ext JS Library 1.1.1
35267  * Copyright(c) 2006-2007, Ext JS, LLC.
35268  *
35269  * Originally Released Under LGPL - original licence link has changed is not relivant.
35270  *
35271  * Fork - LGPL
35272  * <script type="text/javascript">
35273  */
35274  
35275 /**
35276  * @class Roo.form.Field
35277  * @extends Roo.BoxComponent
35278  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35279  * @constructor
35280  * Creates a new Field
35281  * @param {Object} config Configuration options
35282  */
35283 Roo.form.Field = function(config){
35284     Roo.form.Field.superclass.constructor.call(this, config);
35285 };
35286
35287 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35288     /**
35289      * @cfg {String} fieldLabel Label to use when rendering a form.
35290      */
35291        /**
35292      * @cfg {String} qtip Mouse over tip
35293      */
35294      
35295     /**
35296      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35297      */
35298     invalidClass : "x-form-invalid",
35299     /**
35300      * @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")
35301      */
35302     invalidText : "The value in this field is invalid",
35303     /**
35304      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35305      */
35306     focusClass : "x-form-focus",
35307     /**
35308      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35309       automatic validation (defaults to "keyup").
35310      */
35311     validationEvent : "keyup",
35312     /**
35313      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35314      */
35315     validateOnBlur : true,
35316     /**
35317      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35318      */
35319     validationDelay : 250,
35320     /**
35321      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35322      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35323      */
35324     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35325     /**
35326      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35327      */
35328     fieldClass : "x-form-field",
35329     /**
35330      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35331      *<pre>
35332 Value         Description
35333 -----------   ----------------------------------------------------------------------
35334 qtip          Display a quick tip when the user hovers over the field
35335 title         Display a default browser title attribute popup
35336 under         Add a block div beneath the field containing the error text
35337 side          Add an error icon to the right of the field with a popup on hover
35338 [element id]  Add the error text directly to the innerHTML of the specified element
35339 </pre>
35340      */
35341     msgTarget : 'qtip',
35342     /**
35343      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35344      */
35345     msgFx : 'normal',
35346
35347     /**
35348      * @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.
35349      */
35350     readOnly : false,
35351
35352     /**
35353      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35354      */
35355     disabled : false,
35356
35357     /**
35358      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35359      */
35360     inputType : undefined,
35361     
35362     /**
35363      * @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).
35364          */
35365         tabIndex : undefined,
35366         
35367     // private
35368     isFormField : true,
35369
35370     // private
35371     hasFocus : false,
35372     /**
35373      * @property {Roo.Element} fieldEl
35374      * Element Containing the rendered Field (with label etc.)
35375      */
35376     /**
35377      * @cfg {Mixed} value A value to initialize this field with.
35378      */
35379     value : undefined,
35380
35381     /**
35382      * @cfg {String} name The field's HTML name attribute.
35383      */
35384     /**
35385      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35386      */
35387
35388         // private ??
35389         initComponent : function(){
35390         Roo.form.Field.superclass.initComponent.call(this);
35391         this.addEvents({
35392             /**
35393              * @event focus
35394              * Fires when this field receives input focus.
35395              * @param {Roo.form.Field} this
35396              */
35397             focus : true,
35398             /**
35399              * @event blur
35400              * Fires when this field loses input focus.
35401              * @param {Roo.form.Field} this
35402              */
35403             blur : true,
35404             /**
35405              * @event specialkey
35406              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35407              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35408              * @param {Roo.form.Field} this
35409              * @param {Roo.EventObject} e The event object
35410              */
35411             specialkey : true,
35412             /**
35413              * @event change
35414              * Fires just before the field blurs if the field value has changed.
35415              * @param {Roo.form.Field} this
35416              * @param {Mixed} newValue The new value
35417              * @param {Mixed} oldValue The original value
35418              */
35419             change : true,
35420             /**
35421              * @event invalid
35422              * Fires after the field has been marked as invalid.
35423              * @param {Roo.form.Field} this
35424              * @param {String} msg The validation message
35425              */
35426             invalid : true,
35427             /**
35428              * @event valid
35429              * Fires after the field has been validated with no errors.
35430              * @param {Roo.form.Field} this
35431              */
35432             valid : true,
35433              /**
35434              * @event keyup
35435              * Fires after the key up
35436              * @param {Roo.form.Field} this
35437              * @param {Roo.EventObject}  e The event Object
35438              */
35439             keyup : true
35440         });
35441     },
35442
35443     /**
35444      * Returns the name attribute of the field if available
35445      * @return {String} name The field name
35446      */
35447     getName: function(){
35448          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35449     },
35450
35451     // private
35452     onRender : function(ct, position){
35453         Roo.form.Field.superclass.onRender.call(this, ct, position);
35454         if(!this.el){
35455             var cfg = this.getAutoCreate();
35456             if(!cfg.name){
35457                 cfg.name = this.name || this.id;
35458             }
35459             if(this.inputType){
35460                 cfg.type = this.inputType;
35461             }
35462             this.el = ct.createChild(cfg, position);
35463         }
35464         var type = this.el.dom.type;
35465         if(type){
35466             if(type == 'password'){
35467                 type = 'text';
35468             }
35469             this.el.addClass('x-form-'+type);
35470         }
35471         if(this.readOnly){
35472             this.el.dom.readOnly = true;
35473         }
35474         if(this.tabIndex !== undefined){
35475             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35476         }
35477
35478         this.el.addClass([this.fieldClass, this.cls]);
35479         this.initValue();
35480     },
35481
35482     /**
35483      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35484      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35485      * @return {Roo.form.Field} this
35486      */
35487     applyTo : function(target){
35488         this.allowDomMove = false;
35489         this.el = Roo.get(target);
35490         this.render(this.el.dom.parentNode);
35491         return this;
35492     },
35493
35494     // private
35495     initValue : function(){
35496         if(this.value !== undefined){
35497             this.setValue(this.value);
35498         }else if(this.el.dom.value.length > 0){
35499             this.setValue(this.el.dom.value);
35500         }
35501     },
35502
35503     /**
35504      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35505      */
35506     isDirty : function() {
35507         if(this.disabled) {
35508             return false;
35509         }
35510         return String(this.getValue()) !== String(this.originalValue);
35511     },
35512
35513     // private
35514     afterRender : function(){
35515         Roo.form.Field.superclass.afterRender.call(this);
35516         this.initEvents();
35517     },
35518
35519     // private
35520     fireKey : function(e){
35521         //Roo.log('field ' + e.getKey());
35522         if(e.isNavKeyPress()){
35523             this.fireEvent("specialkey", this, e);
35524         }
35525     },
35526
35527     /**
35528      * Resets the current field value to the originally loaded value and clears any validation messages
35529      */
35530     reset : function(){
35531         this.setValue(this.originalValue);
35532         this.clearInvalid();
35533     },
35534
35535     // private
35536     initEvents : function(){
35537         // safari killled keypress - so keydown is now used..
35538         this.el.on("keydown" , this.fireKey,  this);
35539         this.el.on("focus", this.onFocus,  this);
35540         this.el.on("blur", this.onBlur,  this);
35541         this.el.relayEvent('keyup', this);
35542
35543         // reference to original value for reset
35544         this.originalValue = this.getValue();
35545     },
35546
35547     // private
35548     onFocus : function(){
35549         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35550             this.el.addClass(this.focusClass);
35551         }
35552         if(!this.hasFocus){
35553             this.hasFocus = true;
35554             this.startValue = this.getValue();
35555             this.fireEvent("focus", this);
35556         }
35557     },
35558
35559     beforeBlur : Roo.emptyFn,
35560
35561     // private
35562     onBlur : function(){
35563         this.beforeBlur();
35564         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35565             this.el.removeClass(this.focusClass);
35566         }
35567         this.hasFocus = false;
35568         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35569             this.validate();
35570         }
35571         var v = this.getValue();
35572         if(String(v) !== String(this.startValue)){
35573             this.fireEvent('change', this, v, this.startValue);
35574         }
35575         this.fireEvent("blur", this);
35576     },
35577
35578     /**
35579      * Returns whether or not the field value is currently valid
35580      * @param {Boolean} preventMark True to disable marking the field invalid
35581      * @return {Boolean} True if the value is valid, else false
35582      */
35583     isValid : function(preventMark){
35584         if(this.disabled){
35585             return true;
35586         }
35587         var restore = this.preventMark;
35588         this.preventMark = preventMark === true;
35589         var v = this.validateValue(this.processValue(this.getRawValue()));
35590         this.preventMark = restore;
35591         return v;
35592     },
35593
35594     /**
35595      * Validates the field value
35596      * @return {Boolean} True if the value is valid, else false
35597      */
35598     validate : function(){
35599         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35600             this.clearInvalid();
35601             return true;
35602         }
35603         return false;
35604     },
35605
35606     processValue : function(value){
35607         return value;
35608     },
35609
35610     // private
35611     // Subclasses should provide the validation implementation by overriding this
35612     validateValue : function(value){
35613         return true;
35614     },
35615
35616     /**
35617      * Mark this field as invalid
35618      * @param {String} msg The validation message
35619      */
35620     markInvalid : function(msg){
35621         if(!this.rendered || this.preventMark){ // not rendered
35622             return;
35623         }
35624         this.el.addClass(this.invalidClass);
35625         msg = msg || this.invalidText;
35626         switch(this.msgTarget){
35627             case 'qtip':
35628                 this.el.dom.qtip = msg;
35629                 this.el.dom.qclass = 'x-form-invalid-tip';
35630                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35631                     Roo.QuickTips.enable();
35632                 }
35633                 break;
35634             case 'title':
35635                 this.el.dom.title = msg;
35636                 break;
35637             case 'under':
35638                 if(!this.errorEl){
35639                     var elp = this.el.findParent('.x-form-element', 5, true);
35640                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35641                     this.errorEl.setWidth(elp.getWidth(true)-20);
35642                 }
35643                 this.errorEl.update(msg);
35644                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35645                 break;
35646             case 'side':
35647                 if(!this.errorIcon){
35648                     var elp = this.el.findParent('.x-form-element', 5, true);
35649                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35650                 }
35651                 this.alignErrorIcon();
35652                 this.errorIcon.dom.qtip = msg;
35653                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35654                 this.errorIcon.show();
35655                 this.on('resize', this.alignErrorIcon, this);
35656                 break;
35657             default:
35658                 var t = Roo.getDom(this.msgTarget);
35659                 t.innerHTML = msg;
35660                 t.style.display = this.msgDisplay;
35661                 break;
35662         }
35663         this.fireEvent('invalid', this, msg);
35664     },
35665
35666     // private
35667     alignErrorIcon : function(){
35668         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35669     },
35670
35671     /**
35672      * Clear any invalid styles/messages for this field
35673      */
35674     clearInvalid : function(){
35675         if(!this.rendered || this.preventMark){ // not rendered
35676             return;
35677         }
35678         this.el.removeClass(this.invalidClass);
35679         switch(this.msgTarget){
35680             case 'qtip':
35681                 this.el.dom.qtip = '';
35682                 break;
35683             case 'title':
35684                 this.el.dom.title = '';
35685                 break;
35686             case 'under':
35687                 if(this.errorEl){
35688                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35689                 }
35690                 break;
35691             case 'side':
35692                 if(this.errorIcon){
35693                     this.errorIcon.dom.qtip = '';
35694                     this.errorIcon.hide();
35695                     this.un('resize', this.alignErrorIcon, this);
35696                 }
35697                 break;
35698             default:
35699                 var t = Roo.getDom(this.msgTarget);
35700                 t.innerHTML = '';
35701                 t.style.display = 'none';
35702                 break;
35703         }
35704         this.fireEvent('valid', this);
35705     },
35706
35707     /**
35708      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35709      * @return {Mixed} value The field value
35710      */
35711     getRawValue : function(){
35712         var v = this.el.getValue();
35713         if(v === this.emptyText){
35714             v = '';
35715         }
35716         return v;
35717     },
35718
35719     /**
35720      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35721      * @return {Mixed} value The field value
35722      */
35723     getValue : function(){
35724         var v = this.el.getValue();
35725         if(v === this.emptyText || v === undefined){
35726             v = '';
35727         }
35728         return v;
35729     },
35730
35731     /**
35732      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35733      * @param {Mixed} value The value to set
35734      */
35735     setRawValue : function(v){
35736         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35737     },
35738
35739     /**
35740      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35741      * @param {Mixed} value The value to set
35742      */
35743     setValue : function(v){
35744         this.value = v;
35745         if(this.rendered){
35746             this.el.dom.value = (v === null || v === undefined ? '' : v);
35747             this.validate();
35748         }
35749     },
35750
35751     adjustSize : function(w, h){
35752         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35753         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35754         return s;
35755     },
35756
35757     adjustWidth : function(tag, w){
35758         tag = tag.toLowerCase();
35759         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35760             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35761                 if(tag == 'input'){
35762                     return w + 2;
35763                 }
35764                 if(tag = 'textarea'){
35765                     return w-2;
35766                 }
35767             }else if(Roo.isOpera){
35768                 if(tag == 'input'){
35769                     return w + 2;
35770                 }
35771                 if(tag = 'textarea'){
35772                     return w-2;
35773                 }
35774             }
35775         }
35776         return w;
35777     }
35778 });
35779
35780
35781 // anything other than normal should be considered experimental
35782 Roo.form.Field.msgFx = {
35783     normal : {
35784         show: function(msgEl, f){
35785             msgEl.setDisplayed('block');
35786         },
35787
35788         hide : function(msgEl, f){
35789             msgEl.setDisplayed(false).update('');
35790         }
35791     },
35792
35793     slide : {
35794         show: function(msgEl, f){
35795             msgEl.slideIn('t', {stopFx:true});
35796         },
35797
35798         hide : function(msgEl, f){
35799             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35800         }
35801     },
35802
35803     slideRight : {
35804         show: function(msgEl, f){
35805             msgEl.fixDisplay();
35806             msgEl.alignTo(f.el, 'tl-tr');
35807             msgEl.slideIn('l', {stopFx:true});
35808         },
35809
35810         hide : function(msgEl, f){
35811             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35812         }
35813     }
35814 };/*
35815  * Based on:
35816  * Ext JS Library 1.1.1
35817  * Copyright(c) 2006-2007, Ext JS, LLC.
35818  *
35819  * Originally Released Under LGPL - original licence link has changed is not relivant.
35820  *
35821  * Fork - LGPL
35822  * <script type="text/javascript">
35823  */
35824  
35825
35826 /**
35827  * @class Roo.form.TextField
35828  * @extends Roo.form.Field
35829  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35830  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35831  * @constructor
35832  * Creates a new TextField
35833  * @param {Object} config Configuration options
35834  */
35835 Roo.form.TextField = function(config){
35836     Roo.form.TextField.superclass.constructor.call(this, config);
35837     this.addEvents({
35838         /**
35839          * @event autosize
35840          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35841          * according to the default logic, but this event provides a hook for the developer to apply additional
35842          * logic at runtime to resize the field if needed.
35843              * @param {Roo.form.Field} this This text field
35844              * @param {Number} width The new field width
35845              */
35846         autosize : true
35847     });
35848 };
35849
35850 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35851     /**
35852      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35853      */
35854     grow : false,
35855     /**
35856      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35857      */
35858     growMin : 30,
35859     /**
35860      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35861      */
35862     growMax : 800,
35863     /**
35864      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35865      */
35866     vtype : null,
35867     /**
35868      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35869      */
35870     maskRe : null,
35871     /**
35872      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35873      */
35874     disableKeyFilter : false,
35875     /**
35876      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35877      */
35878     allowBlank : true,
35879     /**
35880      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35881      */
35882     minLength : 0,
35883     /**
35884      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35885      */
35886     maxLength : Number.MAX_VALUE,
35887     /**
35888      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35889      */
35890     minLengthText : "The minimum length for this field is {0}",
35891     /**
35892      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35893      */
35894     maxLengthText : "The maximum length for this field is {0}",
35895     /**
35896      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35897      */
35898     selectOnFocus : false,
35899     /**
35900      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35901      */
35902     blankText : "This field is required",
35903     /**
35904      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35905      * If available, this function will be called only after the basic validators all return true, and will be passed the
35906      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35907      */
35908     validator : null,
35909     /**
35910      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35911      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35912      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35913      */
35914     regex : null,
35915     /**
35916      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35917      */
35918     regexText : "",
35919     /**
35920      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35921      */
35922     emptyText : null,
35923     /**
35924      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35925      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35926      */
35927     emptyClass : 'x-form-empty-field',
35928
35929     // private
35930     initEvents : function(){
35931         Roo.form.TextField.superclass.initEvents.call(this);
35932         if(this.validationEvent == 'keyup'){
35933             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35934             this.el.on('keyup', this.filterValidation, this);
35935         }
35936         else if(this.validationEvent !== false){
35937             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35938         }
35939         if(this.selectOnFocus || this.emptyText){
35940             this.on("focus", this.preFocus, this);
35941             if(this.emptyText){
35942                 this.on('blur', this.postBlur, this);
35943                 this.applyEmptyText();
35944             }
35945         }
35946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35947             this.el.on("keypress", this.filterKeys, this);
35948         }
35949         if(this.grow){
35950             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35951             this.el.on("click", this.autoSize,  this);
35952         }
35953     },
35954
35955     processValue : function(value){
35956         if(this.stripCharsRe){
35957             var newValue = value.replace(this.stripCharsRe, '');
35958             if(newValue !== value){
35959                 this.setRawValue(newValue);
35960                 return newValue;
35961             }
35962         }
35963         return value;
35964     },
35965
35966     filterValidation : function(e){
35967         if(!e.isNavKeyPress()){
35968             this.validationTask.delay(this.validationDelay);
35969         }
35970     },
35971
35972     // private
35973     onKeyUp : function(e){
35974         if(!e.isNavKeyPress()){
35975             this.autoSize();
35976         }
35977     },
35978
35979     /**
35980      * Resets the current field value to the originally-loaded value and clears any validation messages.
35981      * Also adds emptyText and emptyClass if the original value was blank.
35982      */
35983     reset : function(){
35984         Roo.form.TextField.superclass.reset.call(this);
35985         this.applyEmptyText();
35986     },
35987
35988     applyEmptyText : function(){
35989         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35990             this.setRawValue(this.emptyText);
35991             this.el.addClass(this.emptyClass);
35992         }
35993     },
35994
35995     // private
35996     preFocus : function(){
35997         if(this.emptyText){
35998             if(this.el.dom.value == this.emptyText){
35999                 this.setRawValue('');
36000             }
36001             this.el.removeClass(this.emptyClass);
36002         }
36003         if(this.selectOnFocus){
36004             this.el.dom.select();
36005         }
36006     },
36007
36008     // private
36009     postBlur : function(){
36010         this.applyEmptyText();
36011     },
36012
36013     // private
36014     filterKeys : function(e){
36015         var k = e.getKey();
36016         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36017             return;
36018         }
36019         var c = e.getCharCode(), cc = String.fromCharCode(c);
36020         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36021             return;
36022         }
36023         if(!this.maskRe.test(cc)){
36024             e.stopEvent();
36025         }
36026     },
36027
36028     setValue : function(v){
36029         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36030             this.el.removeClass(this.emptyClass);
36031         }
36032         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36033         this.applyEmptyText();
36034         this.autoSize();
36035     },
36036
36037     /**
36038      * Validates a value according to the field's validation rules and marks the field as invalid
36039      * if the validation fails
36040      * @param {Mixed} value The value to validate
36041      * @return {Boolean} True if the value is valid, else false
36042      */
36043     validateValue : function(value){
36044         if(value.length < 1 || value === this.emptyText){ // if it's blank
36045              if(this.allowBlank){
36046                 this.clearInvalid();
36047                 return true;
36048              }else{
36049                 this.markInvalid(this.blankText);
36050                 return false;
36051              }
36052         }
36053         if(value.length < this.minLength){
36054             this.markInvalid(String.format(this.minLengthText, this.minLength));
36055             return false;
36056         }
36057         if(value.length > this.maxLength){
36058             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36059             return false;
36060         }
36061         if(this.vtype){
36062             var vt = Roo.form.VTypes;
36063             if(!vt[this.vtype](value, this)){
36064                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36065                 return false;
36066             }
36067         }
36068         if(typeof this.validator == "function"){
36069             var msg = this.validator(value);
36070             if(msg !== true){
36071                 this.markInvalid(msg);
36072                 return false;
36073             }
36074         }
36075         if(this.regex && !this.regex.test(value)){
36076             this.markInvalid(this.regexText);
36077             return false;
36078         }
36079         return true;
36080     },
36081
36082     /**
36083      * Selects text in this field
36084      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36085      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36086      */
36087     selectText : function(start, end){
36088         var v = this.getRawValue();
36089         if(v.length > 0){
36090             start = start === undefined ? 0 : start;
36091             end = end === undefined ? v.length : end;
36092             var d = this.el.dom;
36093             if(d.setSelectionRange){
36094                 d.setSelectionRange(start, end);
36095             }else if(d.createTextRange){
36096                 var range = d.createTextRange();
36097                 range.moveStart("character", start);
36098                 range.moveEnd("character", v.length-end);
36099                 range.select();
36100             }
36101         }
36102     },
36103
36104     /**
36105      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36106      * This only takes effect if grow = true, and fires the autosize event.
36107      */
36108     autoSize : function(){
36109         if(!this.grow || !this.rendered){
36110             return;
36111         }
36112         if(!this.metrics){
36113             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36114         }
36115         var el = this.el;
36116         var v = el.dom.value;
36117         var d = document.createElement('div');
36118         d.appendChild(document.createTextNode(v));
36119         v = d.innerHTML;
36120         d = null;
36121         v += "&#160;";
36122         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36123         this.el.setWidth(w);
36124         this.fireEvent("autosize", this, w);
36125     }
36126 });/*
36127  * Based on:
36128  * Ext JS Library 1.1.1
36129  * Copyright(c) 2006-2007, Ext JS, LLC.
36130  *
36131  * Originally Released Under LGPL - original licence link has changed is not relivant.
36132  *
36133  * Fork - LGPL
36134  * <script type="text/javascript">
36135  */
36136  
36137 /**
36138  * @class Roo.form.Hidden
36139  * @extends Roo.form.TextField
36140  * Simple Hidden element used on forms 
36141  * 
36142  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36143  * 
36144  * @constructor
36145  * Creates a new Hidden form element.
36146  * @param {Object} config Configuration options
36147  */
36148
36149
36150
36151 // easy hidden field...
36152 Roo.form.Hidden = function(config){
36153     Roo.form.Hidden.superclass.constructor.call(this, config);
36154 };
36155   
36156 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36157     fieldLabel:      '',
36158     inputType:      'hidden',
36159     width:          50,
36160     allowBlank:     true,
36161     labelSeparator: '',
36162     hidden:         true,
36163     itemCls :       'x-form-item-display-none'
36164
36165
36166 });
36167
36168
36169 /*
36170  * Based on:
36171  * Ext JS Library 1.1.1
36172  * Copyright(c) 2006-2007, Ext JS, LLC.
36173  *
36174  * Originally Released Under LGPL - original licence link has changed is not relivant.
36175  *
36176  * Fork - LGPL
36177  * <script type="text/javascript">
36178  */
36179  
36180 /**
36181  * @class Roo.form.TriggerField
36182  * @extends Roo.form.TextField
36183  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36184  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36185  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36186  * for which you can provide a custom implementation.  For example:
36187  * <pre><code>
36188 var trigger = new Roo.form.TriggerField();
36189 trigger.onTriggerClick = myTriggerFn;
36190 trigger.applyTo('my-field');
36191 </code></pre>
36192  *
36193  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36194  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36195  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36196  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36197  * @constructor
36198  * Create a new TriggerField.
36199  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36200  * to the base TextField)
36201  */
36202 Roo.form.TriggerField = function(config){
36203     this.mimicing = false;
36204     Roo.form.TriggerField.superclass.constructor.call(this, config);
36205 };
36206
36207 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36208     /**
36209      * @cfg {String} triggerClass A CSS class to apply to the trigger
36210      */
36211     /**
36212      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36213      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36214      */
36215     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36216     /**
36217      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36218      */
36219     hideTrigger:false,
36220
36221     /** @cfg {Boolean} grow @hide */
36222     /** @cfg {Number} growMin @hide */
36223     /** @cfg {Number} growMax @hide */
36224
36225     /**
36226      * @hide 
36227      * @method
36228      */
36229     autoSize: Roo.emptyFn,
36230     // private
36231     monitorTab : true,
36232     // private
36233     deferHeight : true,
36234
36235     
36236     actionMode : 'wrap',
36237     // private
36238     onResize : function(w, h){
36239         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36240         if(typeof w == 'number'){
36241             var x = w - this.trigger.getWidth();
36242             this.el.setWidth(this.adjustWidth('input', x));
36243             this.trigger.setStyle('left', x+'px');
36244         }
36245     },
36246
36247     // private
36248     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36249
36250     // private
36251     getResizeEl : function(){
36252         return this.wrap;
36253     },
36254
36255     // private
36256     getPositionEl : function(){
36257         return this.wrap;
36258     },
36259
36260     // private
36261     alignErrorIcon : function(){
36262         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36263     },
36264
36265     // private
36266     onRender : function(ct, position){
36267         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36268         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36269         this.trigger = this.wrap.createChild(this.triggerConfig ||
36270                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36271         if(this.hideTrigger){
36272             this.trigger.setDisplayed(false);
36273         }
36274         this.initTrigger();
36275         if(!this.width){
36276             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36277         }
36278     },
36279
36280     // private
36281     initTrigger : function(){
36282         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36283         this.trigger.addClassOnOver('x-form-trigger-over');
36284         this.trigger.addClassOnClick('x-form-trigger-click');
36285     },
36286
36287     // private
36288     onDestroy : function(){
36289         if(this.trigger){
36290             this.trigger.removeAllListeners();
36291             this.trigger.remove();
36292         }
36293         if(this.wrap){
36294             this.wrap.remove();
36295         }
36296         Roo.form.TriggerField.superclass.onDestroy.call(this);
36297     },
36298
36299     // private
36300     onFocus : function(){
36301         Roo.form.TriggerField.superclass.onFocus.call(this);
36302         if(!this.mimicing){
36303             this.wrap.addClass('x-trigger-wrap-focus');
36304             this.mimicing = true;
36305             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36306             if(this.monitorTab){
36307                 this.el.on("keydown", this.checkTab, this);
36308             }
36309         }
36310     },
36311
36312     // private
36313     checkTab : function(e){
36314         if(e.getKey() == e.TAB){
36315             this.triggerBlur();
36316         }
36317     },
36318
36319     // private
36320     onBlur : function(){
36321         // do nothing
36322     },
36323
36324     // private
36325     mimicBlur : function(e, t){
36326         if(!this.wrap.contains(t) && this.validateBlur()){
36327             this.triggerBlur();
36328         }
36329     },
36330
36331     // private
36332     triggerBlur : function(){
36333         this.mimicing = false;
36334         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36335         if(this.monitorTab){
36336             this.el.un("keydown", this.checkTab, this);
36337         }
36338         this.wrap.removeClass('x-trigger-wrap-focus');
36339         Roo.form.TriggerField.superclass.onBlur.call(this);
36340     },
36341
36342     // private
36343     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36344     validateBlur : function(e, t){
36345         return true;
36346     },
36347
36348     // private
36349     onDisable : function(){
36350         Roo.form.TriggerField.superclass.onDisable.call(this);
36351         if(this.wrap){
36352             this.wrap.addClass('x-item-disabled');
36353         }
36354     },
36355
36356     // private
36357     onEnable : function(){
36358         Roo.form.TriggerField.superclass.onEnable.call(this);
36359         if(this.wrap){
36360             this.wrap.removeClass('x-item-disabled');
36361         }
36362     },
36363
36364     // private
36365     onShow : function(){
36366         var ae = this.getActionEl();
36367         
36368         if(ae){
36369             ae.dom.style.display = '';
36370             ae.dom.style.visibility = 'visible';
36371         }
36372     },
36373
36374     // private
36375     
36376     onHide : function(){
36377         var ae = this.getActionEl();
36378         ae.dom.style.display = 'none';
36379     },
36380
36381     /**
36382      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36383      * by an implementing function.
36384      * @method
36385      * @param {EventObject} e
36386      */
36387     onTriggerClick : Roo.emptyFn
36388 });
36389
36390 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36391 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36392 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36393 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36394     initComponent : function(){
36395         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36396
36397         this.triggerConfig = {
36398             tag:'span', cls:'x-form-twin-triggers', cn:[
36399             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36400             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36401         ]};
36402     },
36403
36404     getTrigger : function(index){
36405         return this.triggers[index];
36406     },
36407
36408     initTrigger : function(){
36409         var ts = this.trigger.select('.x-form-trigger', true);
36410         this.wrap.setStyle('overflow', 'hidden');
36411         var triggerField = this;
36412         ts.each(function(t, all, index){
36413             t.hide = function(){
36414                 var w = triggerField.wrap.getWidth();
36415                 this.dom.style.display = 'none';
36416                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36417             };
36418             t.show = function(){
36419                 var w = triggerField.wrap.getWidth();
36420                 this.dom.style.display = '';
36421                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36422             };
36423             var triggerIndex = 'Trigger'+(index+1);
36424
36425             if(this['hide'+triggerIndex]){
36426                 t.dom.style.display = 'none';
36427             }
36428             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36429             t.addClassOnOver('x-form-trigger-over');
36430             t.addClassOnClick('x-form-trigger-click');
36431         }, this);
36432         this.triggers = ts.elements;
36433     },
36434
36435     onTrigger1Click : Roo.emptyFn,
36436     onTrigger2Click : Roo.emptyFn
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447  
36448 /**
36449  * @class Roo.form.TextArea
36450  * @extends Roo.form.TextField
36451  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36452  * support for auto-sizing.
36453  * @constructor
36454  * Creates a new TextArea
36455  * @param {Object} config Configuration options
36456  */
36457 Roo.form.TextArea = function(config){
36458     Roo.form.TextArea.superclass.constructor.call(this, config);
36459     // these are provided exchanges for backwards compat
36460     // minHeight/maxHeight were replaced by growMin/growMax to be
36461     // compatible with TextField growing config values
36462     if(this.minHeight !== undefined){
36463         this.growMin = this.minHeight;
36464     }
36465     if(this.maxHeight !== undefined){
36466         this.growMax = this.maxHeight;
36467     }
36468 };
36469
36470 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36471     /**
36472      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36473      */
36474     growMin : 60,
36475     /**
36476      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36477      */
36478     growMax: 1000,
36479     /**
36480      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36481      * in the field (equivalent to setting overflow: hidden, defaults to false)
36482      */
36483     preventScrollbars: false,
36484     /**
36485      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36486      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36487      */
36488
36489     // private
36490     onRender : function(ct, position){
36491         if(!this.el){
36492             this.defaultAutoCreate = {
36493                 tag: "textarea",
36494                 style:"width:300px;height:60px;",
36495                 autocomplete: "off"
36496             };
36497         }
36498         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36499         if(this.grow){
36500             this.textSizeEl = Roo.DomHelper.append(document.body, {
36501                 tag: "pre", cls: "x-form-grow-sizer"
36502             });
36503             if(this.preventScrollbars){
36504                 this.el.setStyle("overflow", "hidden");
36505             }
36506             this.el.setHeight(this.growMin);
36507         }
36508     },
36509
36510     onDestroy : function(){
36511         if(this.textSizeEl){
36512             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36513         }
36514         Roo.form.TextArea.superclass.onDestroy.call(this);
36515     },
36516
36517     // private
36518     onKeyUp : function(e){
36519         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36520             this.autoSize();
36521         }
36522     },
36523
36524     /**
36525      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36526      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36527      */
36528     autoSize : function(){
36529         if(!this.grow || !this.textSizeEl){
36530             return;
36531         }
36532         var el = this.el;
36533         var v = el.dom.value;
36534         var ts = this.textSizeEl;
36535
36536         ts.innerHTML = '';
36537         ts.appendChild(document.createTextNode(v));
36538         v = ts.innerHTML;
36539
36540         Roo.fly(ts).setWidth(this.el.getWidth());
36541         if(v.length < 1){
36542             v = "&#160;&#160;";
36543         }else{
36544             if(Roo.isIE){
36545                 v = v.replace(/\n/g, '<p>&#160;</p>');
36546             }
36547             v += "&#160;\n&#160;";
36548         }
36549         ts.innerHTML = v;
36550         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36551         if(h != this.lastHeight){
36552             this.lastHeight = h;
36553             this.el.setHeight(h);
36554             this.fireEvent("autosize", this, h);
36555         }
36556     }
36557 });/*
36558  * Based on:
36559  * Ext JS Library 1.1.1
36560  * Copyright(c) 2006-2007, Ext JS, LLC.
36561  *
36562  * Originally Released Under LGPL - original licence link has changed is not relivant.
36563  *
36564  * Fork - LGPL
36565  * <script type="text/javascript">
36566  */
36567  
36568
36569 /**
36570  * @class Roo.form.NumberField
36571  * @extends Roo.form.TextField
36572  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36573  * @constructor
36574  * Creates a new NumberField
36575  * @param {Object} config Configuration options
36576  */
36577 Roo.form.NumberField = function(config){
36578     Roo.form.NumberField.superclass.constructor.call(this, config);
36579 };
36580
36581 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36582     /**
36583      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36584      */
36585     fieldClass: "x-form-field x-form-num-field",
36586     /**
36587      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36588      */
36589     allowDecimals : true,
36590     /**
36591      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36592      */
36593     decimalSeparator : ".",
36594     /**
36595      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36596      */
36597     decimalPrecision : 2,
36598     /**
36599      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36600      */
36601     allowNegative : true,
36602     /**
36603      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36604      */
36605     minValue : Number.NEGATIVE_INFINITY,
36606     /**
36607      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36608      */
36609     maxValue : Number.MAX_VALUE,
36610     /**
36611      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36612      */
36613     minText : "The minimum value for this field is {0}",
36614     /**
36615      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36616      */
36617     maxText : "The maximum value for this field is {0}",
36618     /**
36619      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36620      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36621      */
36622     nanText : "{0} is not a valid number",
36623
36624     // private
36625     initEvents : function(){
36626         Roo.form.NumberField.superclass.initEvents.call(this);
36627         var allowed = "0123456789";
36628         if(this.allowDecimals){
36629             allowed += this.decimalSeparator;
36630         }
36631         if(this.allowNegative){
36632             allowed += "-";
36633         }
36634         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36635         var keyPress = function(e){
36636             var k = e.getKey();
36637             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36638                 return;
36639             }
36640             var c = e.getCharCode();
36641             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36642                 e.stopEvent();
36643             }
36644         };
36645         this.el.on("keypress", keyPress, this);
36646     },
36647
36648     // private
36649     validateValue : function(value){
36650         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36651             return false;
36652         }
36653         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36654              return true;
36655         }
36656         var num = this.parseValue(value);
36657         if(isNaN(num)){
36658             this.markInvalid(String.format(this.nanText, value));
36659             return false;
36660         }
36661         if(num < this.minValue){
36662             this.markInvalid(String.format(this.minText, this.minValue));
36663             return false;
36664         }
36665         if(num > this.maxValue){
36666             this.markInvalid(String.format(this.maxText, this.maxValue));
36667             return false;
36668         }
36669         return true;
36670     },
36671
36672     getValue : function(){
36673         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36674     },
36675
36676     // private
36677     parseValue : function(value){
36678         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36679         return isNaN(value) ? '' : value;
36680     },
36681
36682     // private
36683     fixPrecision : function(value){
36684         var nan = isNaN(value);
36685         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36686             return nan ? '' : value;
36687         }
36688         return parseFloat(value).toFixed(this.decimalPrecision);
36689     },
36690
36691     setValue : function(v){
36692         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36693     },
36694
36695     // private
36696     decimalPrecisionFcn : function(v){
36697         return Math.floor(v);
36698     },
36699
36700     beforeBlur : function(){
36701         var v = this.parseValue(this.getRawValue());
36702         if(v){
36703             this.setValue(this.fixPrecision(v));
36704         }
36705     }
36706 });/*
36707  * Based on:
36708  * Ext JS Library 1.1.1
36709  * Copyright(c) 2006-2007, Ext JS, LLC.
36710  *
36711  * Originally Released Under LGPL - original licence link has changed is not relivant.
36712  *
36713  * Fork - LGPL
36714  * <script type="text/javascript">
36715  */
36716  
36717 /**
36718  * @class Roo.form.DateField
36719  * @extends Roo.form.TriggerField
36720  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36721 * @constructor
36722 * Create a new DateField
36723 * @param {Object} config
36724  */
36725 Roo.form.DateField = function(config){
36726     Roo.form.DateField.superclass.constructor.call(this, config);
36727     
36728       this.addEvents({
36729          
36730         /**
36731          * @event select
36732          * Fires when a date is selected
36733              * @param {Roo.form.DateField} combo This combo box
36734              * @param {Date} date The date selected
36735              */
36736         'select' : true
36737          
36738     });
36739     
36740     
36741     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36742     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36743     this.ddMatch = null;
36744     if(this.disabledDates){
36745         var dd = this.disabledDates;
36746         var re = "(?:";
36747         for(var i = 0; i < dd.length; i++){
36748             re += dd[i];
36749             if(i != dd.length-1) re += "|";
36750         }
36751         this.ddMatch = new RegExp(re + ")");
36752     }
36753 };
36754
36755 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36756     /**
36757      * @cfg {String} format
36758      * The default date format string which can be overriden for localization support.  The format must be
36759      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36760      */
36761     format : "m/d/y",
36762     /**
36763      * @cfg {String} altFormats
36764      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36765      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36766      */
36767     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36768     /**
36769      * @cfg {Array} disabledDays
36770      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36771      */
36772     disabledDays : null,
36773     /**
36774      * @cfg {String} disabledDaysText
36775      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36776      */
36777     disabledDaysText : "Disabled",
36778     /**
36779      * @cfg {Array} disabledDates
36780      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36781      * expression so they are very powerful. Some examples:
36782      * <ul>
36783      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36784      * <li>["03/08", "09/16"] would disable those days for every year</li>
36785      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36786      * <li>["03/../2006"] would disable every day in March 2006</li>
36787      * <li>["^03"] would disable every day in every March</li>
36788      * </ul>
36789      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36790      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36791      */
36792     disabledDates : null,
36793     /**
36794      * @cfg {String} disabledDatesText
36795      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36796      */
36797     disabledDatesText : "Disabled",
36798     /**
36799      * @cfg {Date/String} minValue
36800      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36801      * valid format (defaults to null).
36802      */
36803     minValue : null,
36804     /**
36805      * @cfg {Date/String} maxValue
36806      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36807      * valid format (defaults to null).
36808      */
36809     maxValue : null,
36810     /**
36811      * @cfg {String} minText
36812      * The error text to display when the date in the cell is before minValue (defaults to
36813      * 'The date in this field must be after {minValue}').
36814      */
36815     minText : "The date in this field must be equal to or after {0}",
36816     /**
36817      * @cfg {String} maxText
36818      * The error text to display when the date in the cell is after maxValue (defaults to
36819      * 'The date in this field must be before {maxValue}').
36820      */
36821     maxText : "The date in this field must be equal to or before {0}",
36822     /**
36823      * @cfg {String} invalidText
36824      * The error text to display when the date in the field is invalid (defaults to
36825      * '{value} is not a valid date - it must be in the format {format}').
36826      */
36827     invalidText : "{0} is not a valid date - it must be in the format {1}",
36828     /**
36829      * @cfg {String} triggerClass
36830      * An additional CSS class used to style the trigger button.  The trigger will always get the
36831      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36832      * which displays a calendar icon).
36833      */
36834     triggerClass : 'x-form-date-trigger',
36835     
36836
36837     /**
36838      * @cfg {bool} useIso
36839      * if enabled, then the date field will use a hidden field to store the 
36840      * real value as iso formated date. default (false)
36841      */ 
36842     useIso : false,
36843     /**
36844      * @cfg {String/Object} autoCreate
36845      * A DomHelper element spec, or true for a default element spec (defaults to
36846      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36847      */ 
36848     // private
36849     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36850     
36851     // private
36852     hiddenField: false,
36853     
36854     onRender : function(ct, position)
36855     {
36856         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36857         if (this.useIso) {
36858             this.el.dom.removeAttribute('name'); 
36859             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36860                     'before', true);
36861             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36862             // prevent input submission
36863             this.hiddenName = this.name;
36864         }
36865             
36866             
36867     },
36868     
36869     // private
36870     validateValue : function(value)
36871     {
36872         value = this.formatDate(value);
36873         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36874             return false;
36875         }
36876         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36877              return true;
36878         }
36879         var svalue = value;
36880         value = this.parseDate(value);
36881         if(!value){
36882             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36883             return false;
36884         }
36885         var time = value.getTime();
36886         if(this.minValue && time < this.minValue.getTime()){
36887             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36888             return false;
36889         }
36890         if(this.maxValue && time > this.maxValue.getTime()){
36891             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36892             return false;
36893         }
36894         if(this.disabledDays){
36895             var day = value.getDay();
36896             for(var i = 0; i < this.disabledDays.length; i++) {
36897                 if(day === this.disabledDays[i]){
36898                     this.markInvalid(this.disabledDaysText);
36899                     return false;
36900                 }
36901             }
36902         }
36903         var fvalue = this.formatDate(value);
36904         if(this.ddMatch && this.ddMatch.test(fvalue)){
36905             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36906             return false;
36907         }
36908         return true;
36909     },
36910
36911     // private
36912     // Provides logic to override the default TriggerField.validateBlur which just returns true
36913     validateBlur : function(){
36914         return !this.menu || !this.menu.isVisible();
36915     },
36916
36917     /**
36918      * Returns the current date value of the date field.
36919      * @return {Date} The date value
36920      */
36921     getValue : function(){
36922         
36923         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36924     },
36925
36926     /**
36927      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36928      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36929      * (the default format used is "m/d/y").
36930      * <br />Usage:
36931      * <pre><code>
36932 //All of these calls set the same date value (May 4, 2006)
36933
36934 //Pass a date object:
36935 var dt = new Date('5/4/06');
36936 dateField.setValue(dt);
36937
36938 //Pass a date string (default format):
36939 dateField.setValue('5/4/06');
36940
36941 //Pass a date string (custom format):
36942 dateField.format = 'Y-m-d';
36943 dateField.setValue('2006-5-4');
36944 </code></pre>
36945      * @param {String/Date} date The date or valid date string
36946      */
36947     setValue : function(date){
36948         if (this.hiddenField) {
36949             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36950         }
36951         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36952     },
36953
36954     // private
36955     parseDate : function(value){
36956         if(!value || value instanceof Date){
36957             return value;
36958         }
36959         var v = Date.parseDate(value, this.format);
36960         if(!v && this.altFormats){
36961             if(!this.altFormatsArray){
36962                 this.altFormatsArray = this.altFormats.split("|");
36963             }
36964             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36965                 v = Date.parseDate(value, this.altFormatsArray[i]);
36966             }
36967         }
36968         return v;
36969     },
36970
36971     // private
36972     formatDate : function(date, fmt){
36973         return (!date || !(date instanceof Date)) ?
36974                date : date.dateFormat(fmt || this.format);
36975     },
36976
36977     // private
36978     menuListeners : {
36979         select: function(m, d){
36980             this.setValue(d);
36981             this.fireEvent('select', this, d);
36982         },
36983         show : function(){ // retain focus styling
36984             this.onFocus();
36985         },
36986         hide : function(){
36987             this.focus.defer(10, this);
36988             var ml = this.menuListeners;
36989             this.menu.un("select", ml.select,  this);
36990             this.menu.un("show", ml.show,  this);
36991             this.menu.un("hide", ml.hide,  this);
36992         }
36993     },
36994
36995     // private
36996     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36997     onTriggerClick : function(){
36998         if(this.disabled){
36999             return;
37000         }
37001         if(this.menu == null){
37002             this.menu = new Roo.menu.DateMenu();
37003         }
37004         Roo.apply(this.menu.picker,  {
37005             showClear: this.allowBlank,
37006             minDate : this.minValue,
37007             maxDate : this.maxValue,
37008             disabledDatesRE : this.ddMatch,
37009             disabledDatesText : this.disabledDatesText,
37010             disabledDays : this.disabledDays,
37011             disabledDaysText : this.disabledDaysText,
37012             format : this.format,
37013             minText : String.format(this.minText, this.formatDate(this.minValue)),
37014             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37015         });
37016         this.menu.on(Roo.apply({}, this.menuListeners, {
37017             scope:this
37018         }));
37019         this.menu.picker.setValue(this.getValue() || new Date());
37020         this.menu.show(this.el, "tl-bl?");
37021     },
37022
37023     beforeBlur : function(){
37024         var v = this.parseDate(this.getRawValue());
37025         if(v){
37026             this.setValue(v);
37027         }
37028     }
37029
37030     /** @cfg {Boolean} grow @hide */
37031     /** @cfg {Number} growMin @hide */
37032     /** @cfg {Number} growMax @hide */
37033     /**
37034      * @hide
37035      * @method autoSize
37036      */
37037 });/*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047  
37048
37049 /**
37050  * @class Roo.form.ComboBox
37051  * @extends Roo.form.TriggerField
37052  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37053  * @constructor
37054  * Create a new ComboBox.
37055  * @param {Object} config Configuration options
37056  */
37057 Roo.form.ComboBox = function(config){
37058     Roo.form.ComboBox.superclass.constructor.call(this, config);
37059     this.addEvents({
37060         /**
37061          * @event expand
37062          * Fires when the dropdown list is expanded
37063              * @param {Roo.form.ComboBox} combo This combo box
37064              */
37065         'expand' : true,
37066         /**
37067          * @event collapse
37068          * Fires when the dropdown list is collapsed
37069              * @param {Roo.form.ComboBox} combo This combo box
37070              */
37071         'collapse' : true,
37072         /**
37073          * @event beforeselect
37074          * Fires before a list item is selected. Return false to cancel the selection.
37075              * @param {Roo.form.ComboBox} combo This combo box
37076              * @param {Roo.data.Record} record The data record returned from the underlying store
37077              * @param {Number} index The index of the selected item in the dropdown list
37078              */
37079         'beforeselect' : true,
37080         /**
37081          * @event select
37082          * Fires when a list item is selected
37083              * @param {Roo.form.ComboBox} combo This combo box
37084              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37085              * @param {Number} index The index of the selected item in the dropdown list
37086              */
37087         'select' : true,
37088         /**
37089          * @event beforequery
37090          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37091          * The event object passed has these properties:
37092              * @param {Roo.form.ComboBox} combo This combo box
37093              * @param {String} query The query
37094              * @param {Boolean} forceAll true to force "all" query
37095              * @param {Boolean} cancel true to cancel the query
37096              * @param {Object} e The query event object
37097              */
37098         'beforequery': true,
37099          /**
37100          * @event add
37101          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37102              * @param {Roo.form.ComboBox} combo This combo box
37103              */
37104         'add' : true,
37105         /**
37106          * @event edit
37107          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37108              * @param {Roo.form.ComboBox} combo This combo box
37109              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37110              */
37111         'edit' : true
37112         
37113         
37114     });
37115     if(this.transform){
37116         this.allowDomMove = false;
37117         var s = Roo.getDom(this.transform);
37118         if(!this.hiddenName){
37119             this.hiddenName = s.name;
37120         }
37121         if(!this.store){
37122             this.mode = 'local';
37123             var d = [], opts = s.options;
37124             for(var i = 0, len = opts.length;i < len; i++){
37125                 var o = opts[i];
37126                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37127                 if(o.selected) {
37128                     this.value = value;
37129                 }
37130                 d.push([value, o.text]);
37131             }
37132             this.store = new Roo.data.SimpleStore({
37133                 'id': 0,
37134                 fields: ['value', 'text'],
37135                 data : d
37136             });
37137             this.valueField = 'value';
37138             this.displayField = 'text';
37139         }
37140         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37141         if(!this.lazyRender){
37142             this.target = true;
37143             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37144             s.parentNode.removeChild(s); // remove it
37145             this.render(this.el.parentNode);
37146         }else{
37147             s.parentNode.removeChild(s); // remove it
37148         }
37149
37150     }
37151     if (this.store) {
37152         this.store = Roo.factory(this.store, Roo.data);
37153     }
37154     
37155     this.selectedIndex = -1;
37156     if(this.mode == 'local'){
37157         if(config.queryDelay === undefined){
37158             this.queryDelay = 10;
37159         }
37160         if(config.minChars === undefined){
37161             this.minChars = 0;
37162         }
37163     }
37164 };
37165
37166 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37167     /**
37168      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37169      */
37170     /**
37171      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37172      * rendering into an Roo.Editor, defaults to false)
37173      */
37174     /**
37175      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37176      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37177      */
37178     /**
37179      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37180      */
37181     /**
37182      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37183      * the dropdown list (defaults to undefined, with no header element)
37184      */
37185
37186      /**
37187      * @cfg {String/Roo.Template} tpl The template to use to render the output
37188      */
37189      
37190     // private
37191     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37192     /**
37193      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37194      */
37195     listWidth: undefined,
37196     /**
37197      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37198      * mode = 'remote' or 'text' if mode = 'local')
37199      */
37200     displayField: undefined,
37201     /**
37202      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37203      * mode = 'remote' or 'value' if mode = 'local'). 
37204      * Note: use of a valueField requires the user make a selection
37205      * in order for a value to be mapped.
37206      */
37207     valueField: undefined,
37208     /**
37209      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37210      * field's data value (defaults to the underlying DOM element's name)
37211      */
37212     hiddenName: undefined,
37213     /**
37214      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37215      */
37216     listClass: '',
37217     /**
37218      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37219      */
37220     selectedClass: 'x-combo-selected',
37221     /**
37222      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37223      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37224      * which displays a downward arrow icon).
37225      */
37226     triggerClass : 'x-form-arrow-trigger',
37227     /**
37228      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37229      */
37230     shadow:'sides',
37231     /**
37232      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37233      * anchor positions (defaults to 'tl-bl')
37234      */
37235     listAlign: 'tl-bl?',
37236     /**
37237      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37238      */
37239     maxHeight: 300,
37240     /**
37241      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37242      * query specified by the allQuery config option (defaults to 'query')
37243      */
37244     triggerAction: 'query',
37245     /**
37246      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37247      * (defaults to 4, does not apply if editable = false)
37248      */
37249     minChars : 4,
37250     /**
37251      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37252      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37253      */
37254     typeAhead: false,
37255     /**
37256      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37257      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37258      */
37259     queryDelay: 500,
37260     /**
37261      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37262      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37263      */
37264     pageSize: 0,
37265     /**
37266      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37267      * when editable = true (defaults to false)
37268      */
37269     selectOnFocus:false,
37270     /**
37271      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37272      */
37273     queryParam: 'query',
37274     /**
37275      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37276      * when mode = 'remote' (defaults to 'Loading...')
37277      */
37278     loadingText: 'Loading...',
37279     /**
37280      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37281      */
37282     resizable: false,
37283     /**
37284      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37285      */
37286     handleHeight : 8,
37287     /**
37288      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37289      * traditional select (defaults to true)
37290      */
37291     editable: true,
37292     /**
37293      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37294      */
37295     allQuery: '',
37296     /**
37297      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37298      */
37299     mode: 'remote',
37300     /**
37301      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37302      * listWidth has a higher value)
37303      */
37304     minListWidth : 70,
37305     /**
37306      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37307      * allow the user to set arbitrary text into the field (defaults to false)
37308      */
37309     forceSelection:false,
37310     /**
37311      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37312      * if typeAhead = true (defaults to 250)
37313      */
37314     typeAheadDelay : 250,
37315     /**
37316      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37317      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37318      */
37319     valueNotFoundText : undefined,
37320     /**
37321      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37322      */
37323     blockFocus : false,
37324     
37325     /**
37326      * @cfg {Boolean} disableClear Disable showing of clear button.
37327      */
37328     disableClear : false,
37329     /**
37330      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37331      */
37332     alwaysQuery : false,
37333     
37334     //private
37335     addicon : false,
37336     editicon: false,
37337     
37338     
37339     // private
37340     onRender : function(ct, position){
37341         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37342         if(this.hiddenName){
37343             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37344                     'before', true);
37345             this.hiddenField.value =
37346                 this.hiddenValue !== undefined ? this.hiddenValue :
37347                 this.value !== undefined ? this.value : '';
37348
37349             // prevent input submission
37350             this.el.dom.removeAttribute('name');
37351         }
37352         if(Roo.isGecko){
37353             this.el.dom.setAttribute('autocomplete', 'off');
37354         }
37355
37356         var cls = 'x-combo-list';
37357
37358         this.list = new Roo.Layer({
37359             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37360         });
37361
37362         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37363         this.list.setWidth(lw);
37364         this.list.swallowEvent('mousewheel');
37365         this.assetHeight = 0;
37366
37367         if(this.title){
37368             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37369             this.assetHeight += this.header.getHeight();
37370         }
37371
37372         this.innerList = this.list.createChild({cls:cls+'-inner'});
37373         this.innerList.on('mouseover', this.onViewOver, this);
37374         this.innerList.on('mousemove', this.onViewMove, this);
37375         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37376         
37377         if(this.allowBlank && !this.pageSize && !this.disableClear){
37378             this.footer = this.list.createChild({cls:cls+'-ft'});
37379             this.pageTb = new Roo.Toolbar(this.footer);
37380            
37381         }
37382         if(this.pageSize){
37383             this.footer = this.list.createChild({cls:cls+'-ft'});
37384             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37385                     {pageSize: this.pageSize});
37386             
37387         }
37388         
37389         if (this.pageTb && this.allowBlank && !this.disableClear) {
37390             var _this = this;
37391             this.pageTb.add(new Roo.Toolbar.Fill(), {
37392                 cls: 'x-btn-icon x-btn-clear',
37393                 text: '&#160;',
37394                 handler: function()
37395                 {
37396                     _this.collapse();
37397                     _this.clearValue();
37398                     _this.onSelect(false, -1);
37399                 }
37400             });
37401         }
37402         if (this.footer) {
37403             this.assetHeight += this.footer.getHeight();
37404         }
37405         
37406
37407         if(!this.tpl){
37408             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37409         }
37410
37411         this.view = new Roo.View(this.innerList, this.tpl, {
37412             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37413         });
37414
37415         this.view.on('click', this.onViewClick, this);
37416
37417         this.store.on('beforeload', this.onBeforeLoad, this);
37418         this.store.on('load', this.onLoad, this);
37419         this.store.on('loadexception', this.collapse, this);
37420
37421         if(this.resizable){
37422             this.resizer = new Roo.Resizable(this.list,  {
37423                pinned:true, handles:'se'
37424             });
37425             this.resizer.on('resize', function(r, w, h){
37426                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37427                 this.listWidth = w;
37428                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37429                 this.restrictHeight();
37430             }, this);
37431             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37432         }
37433         if(!this.editable){
37434             this.editable = true;
37435             this.setEditable(false);
37436         }  
37437         
37438         
37439         if (typeof(this.events.add.listeners) != 'undefined') {
37440             
37441             this.addicon = this.wrap.createChild(
37442                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37443        
37444             this.addicon.on('click', function(e) {
37445                 this.fireEvent('add', this);
37446             }, this);
37447         }
37448         if (typeof(this.events.edit.listeners) != 'undefined') {
37449             
37450             this.editicon = this.wrap.createChild(
37451                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37452             if (this.addicon) {
37453                 this.editicon.setStyle('margin-left', '40px');
37454             }
37455             this.editicon.on('click', function(e) {
37456                 
37457                 // we fire even  if inothing is selected..
37458                 this.fireEvent('edit', this, this.lastData );
37459                 
37460             }, this);
37461         }
37462         
37463         
37464         
37465     },
37466
37467     // private
37468     initEvents : function(){
37469         Roo.form.ComboBox.superclass.initEvents.call(this);
37470
37471         this.keyNav = new Roo.KeyNav(this.el, {
37472             "up" : function(e){
37473                 this.inKeyMode = true;
37474                 this.selectPrev();
37475             },
37476
37477             "down" : function(e){
37478                 if(!this.isExpanded()){
37479                     this.onTriggerClick();
37480                 }else{
37481                     this.inKeyMode = true;
37482                     this.selectNext();
37483                 }
37484             },
37485
37486             "enter" : function(e){
37487                 this.onViewClick();
37488                 //return true;
37489             },
37490
37491             "esc" : function(e){
37492                 this.collapse();
37493             },
37494
37495             "tab" : function(e){
37496                 this.onViewClick(false);
37497                 return true;
37498             },
37499
37500             scope : this,
37501
37502             doRelay : function(foo, bar, hname){
37503                 if(hname == 'down' || this.scope.isExpanded()){
37504                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37505                 }
37506                 return true;
37507             },
37508
37509             forceKeyDown: true
37510         });
37511         this.queryDelay = Math.max(this.queryDelay || 10,
37512                 this.mode == 'local' ? 10 : 250);
37513         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37514         if(this.typeAhead){
37515             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37516         }
37517         if(this.editable !== false){
37518             this.el.on("keyup", this.onKeyUp, this);
37519         }
37520         if(this.forceSelection){
37521             this.on('blur', this.doForce, this);
37522         }
37523     },
37524
37525     onDestroy : function(){
37526         if(this.view){
37527             this.view.setStore(null);
37528             this.view.el.removeAllListeners();
37529             this.view.el.remove();
37530             this.view.purgeListeners();
37531         }
37532         if(this.list){
37533             this.list.destroy();
37534         }
37535         if(this.store){
37536             this.store.un('beforeload', this.onBeforeLoad, this);
37537             this.store.un('load', this.onLoad, this);
37538             this.store.un('loadexception', this.collapse, this);
37539         }
37540         Roo.form.ComboBox.superclass.onDestroy.call(this);
37541     },
37542
37543     // private
37544     fireKey : function(e){
37545         if(e.isNavKeyPress() && !this.list.isVisible()){
37546             this.fireEvent("specialkey", this, e);
37547         }
37548     },
37549
37550     // private
37551     onResize: function(w, h){
37552         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37553         
37554         if(typeof w != 'number'){
37555             // we do not handle it!?!?
37556             return;
37557         }
37558         var tw = this.trigger.getWidth();
37559         tw += this.addicon ? this.addicon.getWidth() : 0;
37560         tw += this.editicon ? this.editicon.getWidth() : 0;
37561         var x = w - tw;
37562         this.el.setWidth( this.adjustWidth('input', x));
37563             
37564         this.trigger.setStyle('left', x+'px');
37565         
37566         if(this.list && this.listWidth === undefined){
37567             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37568             this.list.setWidth(lw);
37569             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37570         }
37571         
37572     
37573         
37574     },
37575
37576     /**
37577      * Allow or prevent the user from directly editing the field text.  If false is passed,
37578      * the user will only be able to select from the items defined in the dropdown list.  This method
37579      * is the runtime equivalent of setting the 'editable' config option at config time.
37580      * @param {Boolean} value True to allow the user to directly edit the field text
37581      */
37582     setEditable : function(value){
37583         if(value == this.editable){
37584             return;
37585         }
37586         this.editable = value;
37587         if(!value){
37588             this.el.dom.setAttribute('readOnly', true);
37589             this.el.on('mousedown', this.onTriggerClick,  this);
37590             this.el.addClass('x-combo-noedit');
37591         }else{
37592             this.el.dom.setAttribute('readOnly', false);
37593             this.el.un('mousedown', this.onTriggerClick,  this);
37594             this.el.removeClass('x-combo-noedit');
37595         }
37596     },
37597
37598     // private
37599     onBeforeLoad : function(){
37600         if(!this.hasFocus){
37601             return;
37602         }
37603         this.innerList.update(this.loadingText ?
37604                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37605         this.restrictHeight();
37606         this.selectedIndex = -1;
37607     },
37608
37609     // private
37610     onLoad : function(){
37611         if(!this.hasFocus){
37612             return;
37613         }
37614         if(this.store.getCount() > 0){
37615             this.expand();
37616             this.restrictHeight();
37617             if(this.lastQuery == this.allQuery){
37618                 if(this.editable){
37619                     this.el.dom.select();
37620                 }
37621                 if(!this.selectByValue(this.value, true)){
37622                     this.select(0, true);
37623                 }
37624             }else{
37625                 this.selectNext();
37626                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37627                     this.taTask.delay(this.typeAheadDelay);
37628                 }
37629             }
37630         }else{
37631             this.onEmptyResults();
37632         }
37633         //this.el.focus();
37634     },
37635
37636     // private
37637     onTypeAhead : function(){
37638         if(this.store.getCount() > 0){
37639             var r = this.store.getAt(0);
37640             var newValue = r.data[this.displayField];
37641             var len = newValue.length;
37642             var selStart = this.getRawValue().length;
37643             if(selStart != len){
37644                 this.setRawValue(newValue);
37645                 this.selectText(selStart, newValue.length);
37646             }
37647         }
37648     },
37649
37650     // private
37651     onSelect : function(record, index){
37652         if(this.fireEvent('beforeselect', this, record, index) !== false){
37653             this.setFromData(index > -1 ? record.data : false);
37654             this.collapse();
37655             this.fireEvent('select', this, record, index);
37656         }
37657     },
37658
37659     /**
37660      * Returns the currently selected field value or empty string if no value is set.
37661      * @return {String} value The selected value
37662      */
37663     getValue : function(){
37664         if(this.valueField){
37665             return typeof this.value != 'undefined' ? this.value : '';
37666         }else{
37667             return Roo.form.ComboBox.superclass.getValue.call(this);
37668         }
37669     },
37670
37671     /**
37672      * Clears any text/value currently set in the field
37673      */
37674     clearValue : function(){
37675         if(this.hiddenField){
37676             this.hiddenField.value = '';
37677         }
37678         this.value = '';
37679         this.setRawValue('');
37680         this.lastSelectionText = '';
37681         this.applyEmptyText();
37682     },
37683
37684     /**
37685      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37686      * will be displayed in the field.  If the value does not match the data value of an existing item,
37687      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37688      * Otherwise the field will be blank (although the value will still be set).
37689      * @param {String} value The value to match
37690      */
37691     setValue : function(v){
37692         var text = v;
37693         if(this.valueField){
37694             var r = this.findRecord(this.valueField, v);
37695             if(r){
37696                 text = r.data[this.displayField];
37697             }else if(this.valueNotFoundText !== undefined){
37698                 text = this.valueNotFoundText;
37699             }
37700         }
37701         this.lastSelectionText = text;
37702         if(this.hiddenField){
37703             this.hiddenField.value = v;
37704         }
37705         Roo.form.ComboBox.superclass.setValue.call(this, text);
37706         this.value = v;
37707     },
37708     /**
37709      * @property {Object} the last set data for the element
37710      */
37711     
37712     lastData : false,
37713     /**
37714      * Sets the value of the field based on a object which is related to the record format for the store.
37715      * @param {Object} value the value to set as. or false on reset?
37716      */
37717     setFromData : function(o){
37718         var dv = ''; // display value
37719         var vv = ''; // value value..
37720         this.lastData = o;
37721         if (this.displayField) {
37722             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37723         } else {
37724             // this is an error condition!!!
37725             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37726         }
37727         
37728         if(this.valueField){
37729             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37730         }
37731         if(this.hiddenField){
37732             this.hiddenField.value = vv;
37733             
37734             this.lastSelectionText = dv;
37735             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37736             this.value = vv;
37737             return;
37738         }
37739         // no hidden field.. - we store the value in 'value', but still display
37740         // display field!!!!
37741         this.lastSelectionText = dv;
37742         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37743         this.value = vv;
37744         
37745         
37746     },
37747     // private
37748     reset : function(){
37749         // overridden so that last data is reset..
37750         this.setValue(this.originalValue);
37751         this.clearInvalid();
37752         this.lastData = false;
37753     },
37754     // private
37755     findRecord : function(prop, value){
37756         var record;
37757         if(this.store.getCount() > 0){
37758             this.store.each(function(r){
37759                 if(r.data[prop] == value){
37760                     record = r;
37761                     return false;
37762                 }
37763             });
37764         }
37765         return record;
37766     },
37767
37768     // private
37769     onViewMove : function(e, t){
37770         this.inKeyMode = false;
37771     },
37772
37773     // private
37774     onViewOver : function(e, t){
37775         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37776             return;
37777         }
37778         var item = this.view.findItemFromChild(t);
37779         if(item){
37780             var index = this.view.indexOf(item);
37781             this.select(index, false);
37782         }
37783     },
37784
37785     // private
37786     onViewClick : function(doFocus){
37787         var index = this.view.getSelectedIndexes()[0];
37788         var r = this.store.getAt(index);
37789         if(r){
37790             this.onSelect(r, index);
37791         }
37792         if(doFocus !== false && !this.blockFocus){
37793             this.el.focus();
37794         }
37795     },
37796
37797     // private
37798     restrictHeight : function(){
37799         this.innerList.dom.style.height = '';
37800         var inner = this.innerList.dom;
37801         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37802         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37803         this.list.beginUpdate();
37804         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37805         this.list.alignTo(this.el, this.listAlign);
37806         this.list.endUpdate();
37807     },
37808
37809     // private
37810     onEmptyResults : function(){
37811         this.collapse();
37812     },
37813
37814     /**
37815      * Returns true if the dropdown list is expanded, else false.
37816      */
37817     isExpanded : function(){
37818         return this.list.isVisible();
37819     },
37820
37821     /**
37822      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37823      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37824      * @param {String} value The data value of the item to select
37825      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37826      * selected item if it is not currently in view (defaults to true)
37827      * @return {Boolean} True if the value matched an item in the list, else false
37828      */
37829     selectByValue : function(v, scrollIntoView){
37830         if(v !== undefined && v !== null){
37831             var r = this.findRecord(this.valueField || this.displayField, v);
37832             if(r){
37833                 this.select(this.store.indexOf(r), scrollIntoView);
37834                 return true;
37835             }
37836         }
37837         return false;
37838     },
37839
37840     /**
37841      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37842      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37843      * @param {Number} index The zero-based index of the list item to select
37844      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37845      * selected item if it is not currently in view (defaults to true)
37846      */
37847     select : function(index, scrollIntoView){
37848         this.selectedIndex = index;
37849         this.view.select(index);
37850         if(scrollIntoView !== false){
37851             var el = this.view.getNode(index);
37852             if(el){
37853                 this.innerList.scrollChildIntoView(el, false);
37854             }
37855         }
37856     },
37857
37858     // private
37859     selectNext : function(){
37860         var ct = this.store.getCount();
37861         if(ct > 0){
37862             if(this.selectedIndex == -1){
37863                 this.select(0);
37864             }else if(this.selectedIndex < ct-1){
37865                 this.select(this.selectedIndex+1);
37866             }
37867         }
37868     },
37869
37870     // private
37871     selectPrev : function(){
37872         var ct = this.store.getCount();
37873         if(ct > 0){
37874             if(this.selectedIndex == -1){
37875                 this.select(0);
37876             }else if(this.selectedIndex != 0){
37877                 this.select(this.selectedIndex-1);
37878             }
37879         }
37880     },
37881
37882     // private
37883     onKeyUp : function(e){
37884         if(this.editable !== false && !e.isSpecialKey()){
37885             this.lastKey = e.getKey();
37886             this.dqTask.delay(this.queryDelay);
37887         }
37888     },
37889
37890     // private
37891     validateBlur : function(){
37892         return !this.list || !this.list.isVisible();   
37893     },
37894
37895     // private
37896     initQuery : function(){
37897         this.doQuery(this.getRawValue());
37898     },
37899
37900     // private
37901     doForce : function(){
37902         if(this.el.dom.value.length > 0){
37903             this.el.dom.value =
37904                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37905             this.applyEmptyText();
37906         }
37907     },
37908
37909     /**
37910      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37911      * query allowing the query action to be canceled if needed.
37912      * @param {String} query The SQL query to execute
37913      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37914      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37915      * saved in the current store (defaults to false)
37916      */
37917     doQuery : function(q, forceAll){
37918         if(q === undefined || q === null){
37919             q = '';
37920         }
37921         var qe = {
37922             query: q,
37923             forceAll: forceAll,
37924             combo: this,
37925             cancel:false
37926         };
37927         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37928             return false;
37929         }
37930         q = qe.query;
37931         forceAll = qe.forceAll;
37932         if(forceAll === true || (q.length >= this.minChars)){
37933             if(this.lastQuery != q || this.alwaysQuery){
37934                 this.lastQuery = q;
37935                 if(this.mode == 'local'){
37936                     this.selectedIndex = -1;
37937                     if(forceAll){
37938                         this.store.clearFilter();
37939                     }else{
37940                         this.store.filter(this.displayField, q);
37941                     }
37942                     this.onLoad();
37943                 }else{
37944                     this.store.baseParams[this.queryParam] = q;
37945                     this.store.load({
37946                         params: this.getParams(q)
37947                     });
37948                     this.expand();
37949                 }
37950             }else{
37951                 this.selectedIndex = -1;
37952                 this.onLoad();   
37953             }
37954         }
37955     },
37956
37957     // private
37958     getParams : function(q){
37959         var p = {};
37960         //p[this.queryParam] = q;
37961         if(this.pageSize){
37962             p.start = 0;
37963             p.limit = this.pageSize;
37964         }
37965         return p;
37966     },
37967
37968     /**
37969      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37970      */
37971     collapse : function(){
37972         if(!this.isExpanded()){
37973             return;
37974         }
37975         this.list.hide();
37976         Roo.get(document).un('mousedown', this.collapseIf, this);
37977         Roo.get(document).un('mousewheel', this.collapseIf, this);
37978         if (!this.editable) {
37979             Roo.get(document).un('keydown', this.listKeyPress, this);
37980         }
37981         this.fireEvent('collapse', this);
37982     },
37983
37984     // private
37985     collapseIf : function(e){
37986         if(!e.within(this.wrap) && !e.within(this.list)){
37987             this.collapse();
37988         }
37989     },
37990
37991     /**
37992      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37993      */
37994     expand : function(){
37995         if(this.isExpanded() || !this.hasFocus){
37996             return;
37997         }
37998         this.list.alignTo(this.el, this.listAlign);
37999         this.list.show();
38000         Roo.get(document).on('mousedown', this.collapseIf, this);
38001         Roo.get(document).on('mousewheel', this.collapseIf, this);
38002         if (!this.editable) {
38003             Roo.get(document).on('keydown', this.listKeyPress, this);
38004         }
38005         
38006         this.fireEvent('expand', this);
38007     },
38008
38009     // private
38010     // Implements the default empty TriggerField.onTriggerClick function
38011     onTriggerClick : function(){
38012         if(this.disabled){
38013             return;
38014         }
38015         if(this.isExpanded()){
38016             this.collapse();
38017             if (!this.blockFocus) {
38018                 this.el.focus();
38019             }
38020             
38021         }else {
38022             this.hasFocus = true;
38023             if(this.triggerAction == 'all') {
38024                 this.doQuery(this.allQuery, true);
38025             } else {
38026                 this.doQuery(this.getRawValue());
38027             }
38028             if (!this.blockFocus) {
38029                 this.el.focus();
38030             }
38031         }
38032     },
38033     listKeyPress : function(e)
38034     {
38035         //Roo.log('listkeypress');
38036         // scroll to first matching element based on key pres..
38037         if (e.isSpecialKey()) {
38038             return false;
38039         }
38040         var k = String.fromCharCode(e.getKey()).toUpperCase();
38041         //Roo.log(k);
38042         var match  = false;
38043         var csel = this.view.getSelectedNodes();
38044         var cselitem = false;
38045         if (csel.length) {
38046             var ix = this.view.indexOf(csel[0]);
38047             cselitem  = this.store.getAt(ix);
38048             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38049                 cselitem = false;
38050             }
38051             
38052         }
38053         
38054         this.store.each(function(v) { 
38055             if (cselitem) {
38056                 // start at existing selection.
38057                 if (cselitem.id == v.id) {
38058                     cselitem = false;
38059                 }
38060                 return;
38061             }
38062                 
38063             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38064                 match = this.store.indexOf(v);
38065                 return false;
38066             }
38067         }, this);
38068         
38069         if (match === false) {
38070             return true; // no more action?
38071         }
38072         // scroll to?
38073         this.view.select(match);
38074         var sn = Roo.get(this.view.getSelectedNodes()[0])
38075         sn.scrollIntoView(sn.dom.parentNode, false);
38076     }
38077
38078     /** 
38079     * @cfg {Boolean} grow 
38080     * @hide 
38081     */
38082     /** 
38083     * @cfg {Number} growMin 
38084     * @hide 
38085     */
38086     /** 
38087     * @cfg {Number} growMax 
38088     * @hide 
38089     */
38090     /**
38091      * @hide
38092      * @method autoSize
38093      */
38094 });/*
38095  * Based on:
38096  * Ext JS Library 1.1.1
38097  * Copyright(c) 2006-2007, Ext JS, LLC.
38098  *
38099  * Originally Released Under LGPL - original licence link has changed is not relivant.
38100  *
38101  * Fork - LGPL
38102  * <script type="text/javascript">
38103  */
38104 /**
38105  * @class Roo.form.Checkbox
38106  * @extends Roo.form.Field
38107  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38108  * @constructor
38109  * Creates a new Checkbox
38110  * @param {Object} config Configuration options
38111  */
38112 Roo.form.Checkbox = function(config){
38113     Roo.form.Checkbox.superclass.constructor.call(this, config);
38114     this.addEvents({
38115         /**
38116          * @event check
38117          * Fires when the checkbox is checked or unchecked.
38118              * @param {Roo.form.Checkbox} this This checkbox
38119              * @param {Boolean} checked The new checked value
38120              */
38121         check : true
38122     });
38123 };
38124
38125 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38126     /**
38127      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38128      */
38129     focusClass : undefined,
38130     /**
38131      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38132      */
38133     fieldClass: "x-form-field",
38134     /**
38135      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38136      */
38137     checked: false,
38138     /**
38139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38140      * {tag: "input", type: "checkbox", autocomplete: "off"})
38141      */
38142     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38143     /**
38144      * @cfg {String} boxLabel The text that appears beside the checkbox
38145      */
38146     boxLabel : "",
38147     /**
38148      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38149      */  
38150     inputValue : '1',
38151     /**
38152      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38153      */
38154      valueOff: '0', // value when not checked..
38155
38156     actionMode : 'viewEl', 
38157     //
38158     // private
38159     itemCls : 'x-menu-check-item x-form-item',
38160     groupClass : 'x-menu-group-item',
38161     inputType : 'hidden',
38162     
38163     
38164     inSetChecked: false, // check that we are not calling self...
38165     
38166     inputElement: false, // real input element?
38167     basedOn: false, // ????
38168     
38169     isFormField: true, // not sure where this is needed!!!!
38170
38171     onResize : function(){
38172         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38173         if(!this.boxLabel){
38174             this.el.alignTo(this.wrap, 'c-c');
38175         }
38176     },
38177
38178     initEvents : function(){
38179         Roo.form.Checkbox.superclass.initEvents.call(this);
38180         this.el.on("click", this.onClick,  this);
38181         this.el.on("change", this.onClick,  this);
38182     },
38183
38184
38185     getResizeEl : function(){
38186         return this.wrap;
38187     },
38188
38189     getPositionEl : function(){
38190         return this.wrap;
38191     },
38192
38193     // private
38194     onRender : function(ct, position){
38195         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38196         /*
38197         if(this.inputValue !== undefined){
38198             this.el.dom.value = this.inputValue;
38199         }
38200         */
38201         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38202         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38203         var viewEl = this.wrap.createChild({ 
38204             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38205         this.viewEl = viewEl;   
38206         this.wrap.on('click', this.onClick,  this); 
38207         
38208         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38209         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38210         
38211         
38212         
38213         if(this.boxLabel){
38214             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38215         //    viewEl.on('click', this.onClick,  this); 
38216         }
38217         //if(this.checked){
38218             this.setChecked(this.checked);
38219         //}else{
38220             //this.checked = this.el.dom;
38221         //}
38222
38223     },
38224
38225     // private
38226     initValue : Roo.emptyFn,
38227
38228     /**
38229      * Returns the checked state of the checkbox.
38230      * @return {Boolean} True if checked, else false
38231      */
38232     getValue : function(){
38233         if(this.el){
38234             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38235         }
38236         return this.valueOff;
38237         
38238     },
38239
38240         // private
38241     onClick : function(){ 
38242         this.setChecked(!this.checked);
38243
38244         //if(this.el.dom.checked != this.checked){
38245         //    this.setValue(this.el.dom.checked);
38246        // }
38247     },
38248
38249     /**
38250      * Sets the checked state of the checkbox.
38251      * On is always based on a string comparison between inputValue and the param.
38252      * @param {Boolean/String} value - the value to set 
38253      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38254      */
38255     setValue : function(v,suppressEvent){
38256         
38257         
38258         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38259         //if(this.el && this.el.dom){
38260         //    this.el.dom.checked = this.checked;
38261         //    this.el.dom.defaultChecked = this.checked;
38262         //}
38263         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38264         //this.fireEvent("check", this, this.checked);
38265     },
38266     // private..
38267     setChecked : function(state,suppressEvent)
38268     {
38269         if (this.inSetChecked) {
38270             this.checked = state;
38271             return;
38272         }
38273         
38274     
38275         if(this.wrap){
38276             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38277         }
38278         this.checked = state;
38279         if(suppressEvent !== true){
38280             this.fireEvent('check', this, state);
38281         }
38282         this.inSetChecked = true;
38283         this.el.dom.value = state ? this.inputValue : this.valueOff;
38284         this.inSetChecked = false;
38285         
38286     },
38287     // handle setting of hidden value by some other method!!?!?
38288     setFromHidden: function()
38289     {
38290         if(!this.el){
38291             return;
38292         }
38293         //console.log("SET FROM HIDDEN");
38294         //alert('setFrom hidden');
38295         this.setValue(this.el.dom.value);
38296     },
38297     
38298     onDestroy : function()
38299     {
38300         if(this.viewEl){
38301             Roo.get(this.viewEl).remove();
38302         }
38303          
38304         Roo.form.Checkbox.superclass.onDestroy.call(this);
38305     }
38306
38307 });/*
38308  * Based on:
38309  * Ext JS Library 1.1.1
38310  * Copyright(c) 2006-2007, Ext JS, LLC.
38311  *
38312  * Originally Released Under LGPL - original licence link has changed is not relivant.
38313  *
38314  * Fork - LGPL
38315  * <script type="text/javascript">
38316  */
38317  
38318 /**
38319  * @class Roo.form.Radio
38320  * @extends Roo.form.Checkbox
38321  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38322  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38323  * @constructor
38324  * Creates a new Radio
38325  * @param {Object} config Configuration options
38326  */
38327 Roo.form.Radio = function(){
38328     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38329 };
38330 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38331     inputType: 'radio',
38332
38333     /**
38334      * If this radio is part of a group, it will return the selected value
38335      * @return {String}
38336      */
38337     getGroupValue : function(){
38338         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38339     }
38340 });//<script type="text/javascript">
38341
38342 /*
38343  * Ext JS Library 1.1.1
38344  * Copyright(c) 2006-2007, Ext JS, LLC.
38345  * licensing@extjs.com
38346  * 
38347  * http://www.extjs.com/license
38348  */
38349  
38350  /*
38351   * 
38352   * Known bugs:
38353   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38354   * - IE ? - no idea how much works there.
38355   * 
38356   * 
38357   * 
38358   */
38359  
38360
38361 /**
38362  * @class Ext.form.HtmlEditor
38363  * @extends Ext.form.Field
38364  * Provides a lightweight HTML Editor component.
38365  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38366  * 
38367  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38368  * supported by this editor.</b><br/><br/>
38369  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38370  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38371  */
38372 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38373       /**
38374      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38375      */
38376     toolbars : false,
38377     /**
38378      * @cfg {String} createLinkText The default text for the create link prompt
38379      */
38380     createLinkText : 'Please enter the URL for the link:',
38381     /**
38382      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38383      */
38384     defaultLinkValue : 'http:/'+'/',
38385    
38386     
38387     // id of frame..
38388     frameId: false,
38389     
38390     // private properties
38391     validationEvent : false,
38392     deferHeight: true,
38393     initialized : false,
38394     activated : false,
38395     sourceEditMode : false,
38396     onFocus : Roo.emptyFn,
38397     iframePad:3,
38398     hideMode:'offsets',
38399     defaultAutoCreate : {
38400         tag: "textarea",
38401         style:"width:500px;height:300px;",
38402         autocomplete: "off"
38403     },
38404
38405     // private
38406     initComponent : function(){
38407         this.addEvents({
38408             /**
38409              * @event initialize
38410              * Fires when the editor is fully initialized (including the iframe)
38411              * @param {HtmlEditor} this
38412              */
38413             initialize: true,
38414             /**
38415              * @event activate
38416              * Fires when the editor is first receives the focus. Any insertion must wait
38417              * until after this event.
38418              * @param {HtmlEditor} this
38419              */
38420             activate: true,
38421              /**
38422              * @event beforesync
38423              * Fires before the textarea is updated with content from the editor iframe. Return false
38424              * to cancel the sync.
38425              * @param {HtmlEditor} this
38426              * @param {String} html
38427              */
38428             beforesync: true,
38429              /**
38430              * @event beforepush
38431              * Fires before the iframe editor is updated with content from the textarea. Return false
38432              * to cancel the push.
38433              * @param {HtmlEditor} this
38434              * @param {String} html
38435              */
38436             beforepush: true,
38437              /**
38438              * @event sync
38439              * Fires when the textarea is updated with content from the editor iframe.
38440              * @param {HtmlEditor} this
38441              * @param {String} html
38442              */
38443             sync: true,
38444              /**
38445              * @event push
38446              * Fires when the iframe editor is updated with content from the textarea.
38447              * @param {HtmlEditor} this
38448              * @param {String} html
38449              */
38450             push: true,
38451              /**
38452              * @event editmodechange
38453              * Fires when the editor switches edit modes
38454              * @param {HtmlEditor} this
38455              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38456              */
38457             editmodechange: true,
38458             /**
38459              * @event editorevent
38460              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38461              * @param {HtmlEditor} this
38462              */
38463             editorevent: true
38464         })
38465     },
38466
38467     /**
38468      * Protected method that will not generally be called directly. It
38469      * is called when the editor creates its toolbar. Override this method if you need to
38470      * add custom toolbar buttons.
38471      * @param {HtmlEditor} editor
38472      */
38473     createToolbar : function(editor){
38474         if (!editor.toolbars || !editor.toolbars.length) {
38475             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38476         }
38477         
38478         for (var i =0 ; i < editor.toolbars.length;i++) {
38479             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38480             editor.toolbars[i].init(editor);
38481         }
38482          
38483         
38484     },
38485
38486     /**
38487      * Protected method that will not generally be called directly. It
38488      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38489      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38490      */
38491     getDocMarkup : function(){
38492         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38493     },
38494
38495     // private
38496     onRender : function(ct, position){
38497         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38498         this.el.dom.style.border = '0 none';
38499         this.el.dom.setAttribute('tabIndex', -1);
38500         this.el.addClass('x-hidden');
38501         if(Roo.isIE){ // fix IE 1px bogus margin
38502             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38503         }
38504         this.wrap = this.el.wrap({
38505             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38506         });
38507
38508         this.frameId = Roo.id();
38509         this.createToolbar(this);
38510         
38511         
38512         
38513         
38514       
38515         
38516         var iframe = this.wrap.createChild({
38517             tag: 'iframe',
38518             id: this.frameId,
38519             name: this.frameId,
38520             frameBorder : 'no',
38521             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38522         });
38523         
38524        // console.log(iframe);
38525         //this.wrap.dom.appendChild(iframe);
38526
38527         this.iframe = iframe.dom;
38528
38529          this.assignDocWin();
38530         
38531         this.doc.designMode = 'on';
38532        
38533         this.doc.open();
38534         this.doc.write(this.getDocMarkup());
38535         this.doc.close();
38536
38537         
38538         var task = { // must defer to wait for browser to be ready
38539             run : function(){
38540                 //console.log("run task?" + this.doc.readyState);
38541                 this.assignDocWin();
38542                 if(this.doc.body || this.doc.readyState == 'complete'){
38543                     try {
38544                         this.doc.designMode="on";
38545                     } catch (e) {
38546                         return;
38547                     }
38548                     Roo.TaskMgr.stop(task);
38549                     this.initEditor.defer(10, this);
38550                 }
38551             },
38552             interval : 10,
38553             duration:10000,
38554             scope: this
38555         };
38556         Roo.TaskMgr.start(task);
38557
38558         if(!this.width){
38559             this.setSize(this.el.getSize());
38560         }
38561     },
38562
38563     // private
38564     onResize : function(w, h){
38565         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38566         if(this.el && this.iframe){
38567             if(typeof w == 'number'){
38568                 var aw = w - this.wrap.getFrameWidth('lr');
38569                 this.el.setWidth(this.adjustWidth('textarea', aw));
38570                 this.iframe.style.width = aw + 'px';
38571             }
38572             if(typeof h == 'number'){
38573                 var tbh = 0;
38574                 for (var i =0; i < this.toolbars.length;i++) {
38575                     // fixme - ask toolbars for heights?
38576                     tbh += this.toolbars[i].tb.el.getHeight();
38577                 }
38578                 
38579                 
38580                 
38581                 
38582                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38583                 this.el.setHeight(this.adjustWidth('textarea', ah));
38584                 this.iframe.style.height = ah + 'px';
38585                 if(this.doc){
38586                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38587                 }
38588             }
38589         }
38590     },
38591
38592     /**
38593      * Toggles the editor between standard and source edit mode.
38594      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38595      */
38596     toggleSourceEdit : function(sourceEditMode){
38597         
38598         this.sourceEditMode = sourceEditMode === true;
38599         
38600         if(this.sourceEditMode){
38601           
38602             this.syncValue();
38603             this.iframe.className = 'x-hidden';
38604             this.el.removeClass('x-hidden');
38605             this.el.dom.removeAttribute('tabIndex');
38606             this.el.focus();
38607         }else{
38608              
38609             this.pushValue();
38610             this.iframe.className = '';
38611             this.el.addClass('x-hidden');
38612             this.el.dom.setAttribute('tabIndex', -1);
38613             this.deferFocus();
38614         }
38615         this.setSize(this.wrap.getSize());
38616         this.fireEvent('editmodechange', this, this.sourceEditMode);
38617     },
38618
38619     // private used internally
38620     createLink : function(){
38621         var url = prompt(this.createLinkText, this.defaultLinkValue);
38622         if(url && url != 'http:/'+'/'){
38623             this.relayCmd('createlink', url);
38624         }
38625     },
38626
38627     // private (for BoxComponent)
38628     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38629
38630     // private (for BoxComponent)
38631     getResizeEl : function(){
38632         return this.wrap;
38633     },
38634
38635     // private (for BoxComponent)
38636     getPositionEl : function(){
38637         return this.wrap;
38638     },
38639
38640     // private
38641     initEvents : function(){
38642         this.originalValue = this.getValue();
38643     },
38644
38645     /**
38646      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38647      * @method
38648      */
38649     markInvalid : Roo.emptyFn,
38650     /**
38651      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38652      * @method
38653      */
38654     clearInvalid : Roo.emptyFn,
38655
38656     setValue : function(v){
38657         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38658         this.pushValue();
38659     },
38660
38661     /**
38662      * Protected method that will not generally be called directly. If you need/want
38663      * custom HTML cleanup, this is the method you should override.
38664      * @param {String} html The HTML to be cleaned
38665      * return {String} The cleaned HTML
38666      */
38667     cleanHtml : function(html){
38668         html = String(html);
38669         if(html.length > 5){
38670             if(Roo.isSafari){ // strip safari nonsense
38671                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38672             }
38673         }
38674         if(html == '&nbsp;'){
38675             html = '';
38676         }
38677         return html;
38678     },
38679
38680     /**
38681      * Protected method that will not generally be called directly. Syncs the contents
38682      * of the editor iframe with the textarea.
38683      */
38684     syncValue : function(){
38685         if(this.initialized){
38686             var bd = (this.doc.body || this.doc.documentElement);
38687             this.cleanUpPaste();
38688             var html = bd.innerHTML;
38689             if(Roo.isSafari){
38690                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38691                 var m = bs.match(/text-align:(.*?);/i);
38692                 if(m && m[1]){
38693                     html = '<div style="'+m[0]+'">' + html + '</div>';
38694                 }
38695             }
38696             html = this.cleanHtml(html);
38697             if(this.fireEvent('beforesync', this, html) !== false){
38698                 this.el.dom.value = html;
38699                 this.fireEvent('sync', this, html);
38700             }
38701         }
38702     },
38703
38704     /**
38705      * Protected method that will not generally be called directly. Pushes the value of the textarea
38706      * into the iframe editor.
38707      */
38708     pushValue : function(){
38709         if(this.initialized){
38710             var v = this.el.dom.value;
38711             if(v.length < 1){
38712                 v = '&#160;';
38713             }
38714             
38715             if(this.fireEvent('beforepush', this, v) !== false){
38716                 var d = (this.doc.body || this.doc.documentElement);
38717                 d.innerHTML = v;
38718                 this.cleanUpPaste();
38719                 this.el.dom.value = d.innerHTML;
38720                 this.fireEvent('push', this, v);
38721             }
38722         }
38723     },
38724
38725     // private
38726     deferFocus : function(){
38727         this.focus.defer(10, this);
38728     },
38729
38730     // doc'ed in Field
38731     focus : function(){
38732         if(this.win && !this.sourceEditMode){
38733             this.win.focus();
38734         }else{
38735             this.el.focus();
38736         }
38737     },
38738     
38739     assignDocWin: function()
38740     {
38741         var iframe = this.iframe;
38742         
38743          if(Roo.isIE){
38744             this.doc = iframe.contentWindow.document;
38745             this.win = iframe.contentWindow;
38746         } else {
38747             if (!Roo.get(this.frameId)) {
38748                 return;
38749             }
38750             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38751             this.win = Roo.get(this.frameId).dom.contentWindow;
38752         }
38753     },
38754     
38755     // private
38756     initEditor : function(){
38757         //console.log("INIT EDITOR");
38758         this.assignDocWin();
38759         
38760         
38761         
38762         this.doc.designMode="on";
38763         this.doc.open();
38764         this.doc.write(this.getDocMarkup());
38765         this.doc.close();
38766         
38767         var dbody = (this.doc.body || this.doc.documentElement);
38768         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38769         // this copies styles from the containing element into thsi one..
38770         // not sure why we need all of this..
38771         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38772         ss['background-attachment'] = 'fixed'; // w3c
38773         dbody.bgProperties = 'fixed'; // ie
38774         Roo.DomHelper.applyStyles(dbody, ss);
38775         Roo.EventManager.on(this.doc, {
38776             'mousedown': this.onEditorEvent,
38777             'dblclick': this.onEditorEvent,
38778             'click': this.onEditorEvent,
38779             'keyup': this.onEditorEvent,
38780             buffer:100,
38781             scope: this
38782         });
38783         if(Roo.isGecko){
38784             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38785         }
38786         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38787             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38788         }
38789         this.initialized = true;
38790
38791         this.fireEvent('initialize', this);
38792         this.pushValue();
38793     },
38794
38795     // private
38796     onDestroy : function(){
38797         
38798         
38799         
38800         if(this.rendered){
38801             
38802             for (var i =0; i < this.toolbars.length;i++) {
38803                 // fixme - ask toolbars for heights?
38804                 this.toolbars[i].onDestroy();
38805             }
38806             
38807             this.wrap.dom.innerHTML = '';
38808             this.wrap.remove();
38809         }
38810     },
38811
38812     // private
38813     onFirstFocus : function(){
38814         
38815         this.assignDocWin();
38816         
38817         
38818         this.activated = true;
38819         for (var i =0; i < this.toolbars.length;i++) {
38820             this.toolbars[i].onFirstFocus();
38821         }
38822        
38823         if(Roo.isGecko){ // prevent silly gecko errors
38824             this.win.focus();
38825             var s = this.win.getSelection();
38826             if(!s.focusNode || s.focusNode.nodeType != 3){
38827                 var r = s.getRangeAt(0);
38828                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38829                 r.collapse(true);
38830                 this.deferFocus();
38831             }
38832             try{
38833                 this.execCmd('useCSS', true);
38834                 this.execCmd('styleWithCSS', false);
38835             }catch(e){}
38836         }
38837         this.fireEvent('activate', this);
38838     },
38839
38840     // private
38841     adjustFont: function(btn){
38842         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38843         //if(Roo.isSafari){ // safari
38844         //    adjust *= 2;
38845        // }
38846         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38847         if(Roo.isSafari){ // safari
38848             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38849             v =  (v < 10) ? 10 : v;
38850             v =  (v > 48) ? 48 : v;
38851             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38852             
38853         }
38854         
38855         
38856         v = Math.max(1, v+adjust);
38857         
38858         this.execCmd('FontSize', v  );
38859     },
38860
38861     onEditorEvent : function(e){
38862         this.fireEvent('editorevent', this, e);
38863       //  this.updateToolbar();
38864         this.syncValue();
38865     },
38866
38867     insertTag : function(tg)
38868     {
38869         // could be a bit smarter... -> wrap the current selected tRoo..
38870         
38871         this.execCmd("formatblock",   tg);
38872         
38873     },
38874     
38875     insertText : function(txt)
38876     {
38877         
38878         
38879         range = this.createRange();
38880         range.deleteContents();
38881                //alert(Sender.getAttribute('label'));
38882                
38883         range.insertNode(this.doc.createTextNode(txt));
38884     } ,
38885     
38886     // private
38887     relayBtnCmd : function(btn){
38888         this.relayCmd(btn.cmd);
38889     },
38890
38891     /**
38892      * Executes a Midas editor command on the editor document and performs necessary focus and
38893      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38894      * @param {String} cmd The Midas command
38895      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38896      */
38897     relayCmd : function(cmd, value){
38898         this.win.focus();
38899         this.execCmd(cmd, value);
38900         this.fireEvent('editorevent', this);
38901         //this.updateToolbar();
38902         this.deferFocus();
38903     },
38904
38905     /**
38906      * Executes a Midas editor command directly on the editor document.
38907      * For visual commands, you should use {@link #relayCmd} instead.
38908      * <b>This should only be called after the editor is initialized.</b>
38909      * @param {String} cmd The Midas command
38910      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38911      */
38912     execCmd : function(cmd, value){
38913         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38914         this.syncValue();
38915     },
38916
38917    
38918     /**
38919      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38920      * to insert tRoo.
38921      * @param {String} text
38922      */
38923     insertAtCursor : function(text){
38924         if(!this.activated){
38925             return;
38926         }
38927         if(Roo.isIE){
38928             this.win.focus();
38929             var r = this.doc.selection.createRange();
38930             if(r){
38931                 r.collapse(true);
38932                 r.pasteHTML(text);
38933                 this.syncValue();
38934                 this.deferFocus();
38935             }
38936         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38937             this.win.focus();
38938             this.execCmd('InsertHTML', text);
38939             this.deferFocus();
38940         }
38941     },
38942  // private
38943     mozKeyPress : function(e){
38944         if(e.ctrlKey){
38945             var c = e.getCharCode(), cmd;
38946           
38947             if(c > 0){
38948                 c = String.fromCharCode(c).toLowerCase();
38949                 switch(c){
38950                     case 'b':
38951                         cmd = 'bold';
38952                     break;
38953                     case 'i':
38954                         cmd = 'italic';
38955                     break;
38956                     case 'u':
38957                         cmd = 'underline';
38958                     case 'v':
38959                         this.cleanUpPaste.defer(100, this);
38960                         return;
38961                     break;
38962                 }
38963                 if(cmd){
38964                     this.win.focus();
38965                     this.execCmd(cmd);
38966                     this.deferFocus();
38967                     e.preventDefault();
38968                 }
38969                 
38970             }
38971         }
38972     },
38973
38974     // private
38975     fixKeys : function(){ // load time branching for fastest keydown performance
38976         if(Roo.isIE){
38977             return function(e){
38978                 var k = e.getKey(), r;
38979                 if(k == e.TAB){
38980                     e.stopEvent();
38981                     r = this.doc.selection.createRange();
38982                     if(r){
38983                         r.collapse(true);
38984                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38985                         this.deferFocus();
38986                     }
38987                     return;
38988                 }
38989                 
38990                 if(k == e.ENTER){
38991                     r = this.doc.selection.createRange();
38992                     if(r){
38993                         var target = r.parentElement();
38994                         if(!target || target.tagName.toLowerCase() != 'li'){
38995                             e.stopEvent();
38996                             r.pasteHTML('<br />');
38997                             r.collapse(false);
38998                             r.select();
38999                         }
39000                     }
39001                 }
39002                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39003                     this.cleanUpPaste.defer(100, this);
39004                     return;
39005                 }
39006                 
39007                 
39008             };
39009         }else if(Roo.isOpera){
39010             return function(e){
39011                 var k = e.getKey();
39012                 if(k == e.TAB){
39013                     e.stopEvent();
39014                     this.win.focus();
39015                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39016                     this.deferFocus();
39017                 }
39018                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39019                     this.cleanUpPaste.defer(100, this);
39020                     return;
39021                 }
39022                 
39023             };
39024         }else if(Roo.isSafari){
39025             return function(e){
39026                 var k = e.getKey();
39027                 
39028                 if(k == e.TAB){
39029                     e.stopEvent();
39030                     this.execCmd('InsertText','\t');
39031                     this.deferFocus();
39032                     return;
39033                 }
39034                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39035                     this.cleanUpPaste.defer(100, this);
39036                     return;
39037                 }
39038                 
39039              };
39040         }
39041     }(),
39042     
39043     getAllAncestors: function()
39044     {
39045         var p = this.getSelectedNode();
39046         var a = [];
39047         if (!p) {
39048             a.push(p); // push blank onto stack..
39049             p = this.getParentElement();
39050         }
39051         
39052         
39053         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39054             a.push(p);
39055             p = p.parentNode;
39056         }
39057         a.push(this.doc.body);
39058         return a;
39059     },
39060     lastSel : false,
39061     lastSelNode : false,
39062     
39063     
39064     getSelection : function() 
39065     {
39066         this.assignDocWin();
39067         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39068     },
39069     
39070     getSelectedNode: function() 
39071     {
39072         // this may only work on Gecko!!!
39073         
39074         // should we cache this!!!!
39075         
39076         
39077         
39078          
39079         var range = this.createRange(this.getSelection());
39080         
39081         if (Roo.isIE) {
39082             var parent = range.parentElement();
39083             while (true) {
39084                 var testRange = range.duplicate();
39085                 testRange.moveToElementText(parent);
39086                 if (testRange.inRange(range)) {
39087                     break;
39088                 }
39089                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39090                     break;
39091                 }
39092                 parent = parent.parentElement;
39093             }
39094             return parent;
39095         }
39096         
39097         
39098         var ar = range.endContainer.childNodes;
39099         if (!ar.length) {
39100             ar = range.commonAncestorContainer.childNodes;
39101             //alert(ar.length);
39102         }
39103         var nodes = [];
39104         var other_nodes = [];
39105         var has_other_nodes = false;
39106         for (var i=0;i<ar.length;i++) {
39107             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39108                 continue;
39109             }
39110             // fullly contained node.
39111             
39112             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39113                 nodes.push(ar[i]);
39114                 continue;
39115             }
39116             
39117             // probably selected..
39118             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39119                 other_nodes.push(ar[i]);
39120                 continue;
39121             }
39122             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39123                 continue;
39124             }
39125             
39126             
39127             has_other_nodes = true;
39128         }
39129         if (!nodes.length && other_nodes.length) {
39130             nodes= other_nodes;
39131         }
39132         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39133             return false;
39134         }
39135         
39136         return nodes[0];
39137     },
39138     createRange: function(sel)
39139     {
39140         // this has strange effects when using with 
39141         // top toolbar - not sure if it's a great idea.
39142         //this.editor.contentWindow.focus();
39143         if (typeof sel != "undefined") {
39144             try {
39145                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39146             } catch(e) {
39147                 return this.doc.createRange();
39148             }
39149         } else {
39150             return this.doc.createRange();
39151         }
39152     },
39153     getParentElement: function()
39154     {
39155         
39156         this.assignDocWin();
39157         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39158         
39159         var range = this.createRange(sel);
39160          
39161         try {
39162             var p = range.commonAncestorContainer;
39163             while (p.nodeType == 3) { // text node
39164                 p = p.parentNode;
39165             }
39166             return p;
39167         } catch (e) {
39168             return null;
39169         }
39170     
39171     },
39172     
39173     
39174     
39175     // BC Hacks - cause I cant work out what i was trying to do..
39176     rangeIntersectsNode : function(range, node)
39177     {
39178         var nodeRange = node.ownerDocument.createRange();
39179         try {
39180             nodeRange.selectNode(node);
39181         }
39182         catch (e) {
39183             nodeRange.selectNodeContents(node);
39184         }
39185
39186         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39187                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39188     },
39189     rangeCompareNode : function(range, node) {
39190         var nodeRange = node.ownerDocument.createRange();
39191         try {
39192             nodeRange.selectNode(node);
39193         } catch (e) {
39194             nodeRange.selectNodeContents(node);
39195         }
39196         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39197         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39198
39199         if (nodeIsBefore && !nodeIsAfter)
39200             return 0;
39201         if (!nodeIsBefore && nodeIsAfter)
39202             return 1;
39203         if (nodeIsBefore && nodeIsAfter)
39204             return 2;
39205
39206         return 3;
39207     },
39208
39209     // private? - in a new class?
39210     cleanUpPaste :  function()
39211     {
39212         // cleans up the whole document..
39213       //  console.log('cleanuppaste');
39214         this.cleanUpChildren(this.doc.body);
39215         
39216         
39217     },
39218     cleanUpChildren : function (n)
39219     {
39220         if (!n.childNodes.length) {
39221             return;
39222         }
39223         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39224            this.cleanUpChild(n.childNodes[i]);
39225         }
39226     },
39227     
39228     
39229         
39230     
39231     cleanUpChild : function (node)
39232     {
39233         //console.log(node);
39234         if (node.nodeName == "#text") {
39235             // clean up silly Windows -- stuff?
39236             return; 
39237         }
39238         if (node.nodeName == "#comment") {
39239             node.parentNode.removeChild(node);
39240             // clean up silly Windows -- stuff?
39241             return; 
39242         }
39243         
39244         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39245             // remove node.
39246             node.parentNode.removeChild(node);
39247             return;
39248             
39249         }
39250         if (!node.attributes || !node.attributes.length) {
39251             this.cleanUpChildren(node);
39252             return;
39253         }
39254         
39255         function cleanAttr(n,v)
39256         {
39257             
39258             if (v.match(/^\./) || v.match(/^\//)) {
39259                 return;
39260             }
39261             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39262                 return;
39263             }
39264             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39265             node.removeAttribute(n);
39266             
39267         }
39268         
39269         function cleanStyle(n,v)
39270         {
39271             if (v.match(/expression/)) { //XSS?? should we even bother..
39272                 node.removeAttribute(n);
39273                 return;
39274             }
39275             
39276             
39277             var parts = v.split(/;/);
39278             Roo.each(parts, function(p) {
39279                 p = p.replace(/\s+/g,'');
39280                 if (!p.length) {
39281                     return;
39282                 }
39283                 var l = p.split(':').shift().replace(/\s+/g,'');
39284                 
39285                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39286                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39287                     node.removeAttribute(n);
39288                     return false;
39289                 }
39290             });
39291             
39292             
39293         }
39294         
39295         
39296         for (var i = node.attributes.length-1; i > -1 ; i--) {
39297             var a = node.attributes[i];
39298             //console.log(a);
39299             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39300                 node.removeAttribute(a.name);
39301                 return;
39302             }
39303             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39304                 cleanAttr(a.name,a.value); // fixme..
39305                 return;
39306             }
39307             if (a.name == 'style') {
39308                 cleanStyle(a.name,a.value);
39309             }
39310             /// clean up MS crap..
39311             if (a.name == 'class') {
39312                 if (a.value.match(/^Mso/)) {
39313                     node.className = '';
39314                 }
39315             }
39316             
39317             // style cleanup!?
39318             // class cleanup?
39319             
39320         }
39321         
39322         
39323         this.cleanUpChildren(node);
39324         
39325         
39326     }
39327     
39328     
39329     // hide stuff that is not compatible
39330     /**
39331      * @event blur
39332      * @hide
39333      */
39334     /**
39335      * @event change
39336      * @hide
39337      */
39338     /**
39339      * @event focus
39340      * @hide
39341      */
39342     /**
39343      * @event specialkey
39344      * @hide
39345      */
39346     /**
39347      * @cfg {String} fieldClass @hide
39348      */
39349     /**
39350      * @cfg {String} focusClass @hide
39351      */
39352     /**
39353      * @cfg {String} autoCreate @hide
39354      */
39355     /**
39356      * @cfg {String} inputType @hide
39357      */
39358     /**
39359      * @cfg {String} invalidClass @hide
39360      */
39361     /**
39362      * @cfg {String} invalidText @hide
39363      */
39364     /**
39365      * @cfg {String} msgFx @hide
39366      */
39367     /**
39368      * @cfg {String} validateOnBlur @hide
39369      */
39370 });
39371
39372 Roo.form.HtmlEditor.white = [
39373         'area', 'br', 'img', 'input', 'hr', 'wbr',
39374         
39375        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39376        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39377        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39378        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39379        'table',   'ul',         'xmp', 
39380        
39381        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39382       'thead',   'tr', 
39383      
39384       'dir', 'menu', 'ol', 'ul', 'dl',
39385        
39386       'embed',  'object'
39387 ];
39388
39389
39390 Roo.form.HtmlEditor.black = [
39391     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39392         'applet', // 
39393         'base',   'basefont', 'bgsound', 'blink',  'body', 
39394         'frame',  'frameset', 'head',    'html',   'ilayer', 
39395         'iframe', 'layer',  'link',     'meta',    'object',   
39396         'script', 'style' ,'title',  'xml' // clean later..
39397 ];
39398 Roo.form.HtmlEditor.clean = [
39399     'script', 'style', 'title', 'xml'
39400 ];
39401
39402 // attributes..
39403
39404 Roo.form.HtmlEditor.ablack = [
39405     'on'
39406 ];
39407     
39408 Roo.form.HtmlEditor.aclean = [ 
39409     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39410 ];
39411
39412 // protocols..
39413 Roo.form.HtmlEditor.pwhite= [
39414         'http',  'https',  'mailto'
39415 ];
39416
39417 Roo.form.HtmlEditor.cwhite= [
39418         'text-align',
39419         'font-size'
39420 ];
39421
39422 // <script type="text/javascript">
39423 /*
39424  * Based on
39425  * Ext JS Library 1.1.1
39426  * Copyright(c) 2006-2007, Ext JS, LLC.
39427  *  
39428  
39429  */
39430
39431 /**
39432  * @class Roo.form.HtmlEditorToolbar1
39433  * Basic Toolbar
39434  * 
39435  * Usage:
39436  *
39437  new Roo.form.HtmlEditor({
39438     ....
39439     toolbars : [
39440         new Roo.form.HtmlEditorToolbar1({
39441             disable : { fonts: 1 , format: 1, ..., ... , ...],
39442             btns : [ .... ]
39443         })
39444     }
39445      
39446  * 
39447  * @cfg {Object} disable List of elements to disable..
39448  * @cfg {Array} btns List of additional buttons.
39449  * 
39450  * 
39451  * NEEDS Extra CSS? 
39452  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39453  */
39454  
39455 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39456 {
39457     
39458     Roo.apply(this, config);
39459     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39460     // dont call parent... till later.
39461 }
39462
39463 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39464     
39465     tb: false,
39466     
39467     rendered: false,
39468     
39469     editor : false,
39470     /**
39471      * @cfg {Object} disable  List of toolbar elements to disable
39472          
39473      */
39474     disable : false,
39475       /**
39476      * @cfg {Array} fontFamilies An array of available font families
39477      */
39478     fontFamilies : [
39479         'Arial',
39480         'Courier New',
39481         'Tahoma',
39482         'Times New Roman',
39483         'Verdana'
39484     ],
39485     
39486     specialChars : [
39487            "&#169;",
39488           "&#174;",     
39489           "&#8482;",    
39490           "&#163;" ,    
39491          // "&#8212;",    
39492           "&#8230;",    
39493           "&#247;" ,    
39494         //  "&#225;" ,     ?? a acute?
39495            "&#8364;"    , //Euro
39496        //   "&#8220;"    ,
39497         //  "&#8221;"    ,
39498         //  "&#8226;"    ,
39499           "&#176;"  //   , // degrees
39500
39501          // "&#233;"     , // e ecute
39502          // "&#250;"     , // u ecute?
39503     ],
39504     inputElements : [ 
39505             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39506             "input:submit", "input:button", "select", "textarea", "label" ],
39507     formats : [
39508         ["p"] ,  
39509         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39510         ["pre"],[ "code"], 
39511         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39512     ],
39513      /**
39514      * @cfg {String} defaultFont default font to use.
39515      */
39516     defaultFont: 'tahoma',
39517    
39518     fontSelect : false,
39519     
39520     
39521     formatCombo : false,
39522     
39523     init : function(editor)
39524     {
39525         this.editor = editor;
39526         
39527         
39528         var fid = editor.frameId;
39529         var etb = this;
39530         function btn(id, toggle, handler){
39531             var xid = fid + '-'+ id ;
39532             return {
39533                 id : xid,
39534                 cmd : id,
39535                 cls : 'x-btn-icon x-edit-'+id,
39536                 enableToggle:toggle !== false,
39537                 scope: editor, // was editor...
39538                 handler:handler||editor.relayBtnCmd,
39539                 clickEvent:'mousedown',
39540                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39541                 tabIndex:-1
39542             };
39543         }
39544         
39545         
39546         
39547         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39548         this.tb = tb;
39549          // stop form submits
39550         tb.el.on('click', function(e){
39551             e.preventDefault(); // what does this do?
39552         });
39553
39554         if(!this.disable.font && !Roo.isSafari){
39555             /* why no safari for fonts
39556             editor.fontSelect = tb.el.createChild({
39557                 tag:'select',
39558                 tabIndex: -1,
39559                 cls:'x-font-select',
39560                 html: editor.createFontOptions()
39561             });
39562             editor.fontSelect.on('change', function(){
39563                 var font = editor.fontSelect.dom.value;
39564                 editor.relayCmd('fontname', font);
39565                 editor.deferFocus();
39566             }, editor);
39567             tb.add(
39568                 editor.fontSelect.dom,
39569                 '-'
39570             );
39571             */
39572         };
39573         if(!this.disable.formats){
39574             this.formatCombo = new Roo.form.ComboBox({
39575                 store: new Roo.data.SimpleStore({
39576                     id : 'tag',
39577                     fields: ['tag'],
39578                     data : this.formats // from states.js
39579                 }),
39580                 blockFocus : true,
39581                 //autoCreate : {tag: "div",  size: "20"},
39582                 displayField:'tag',
39583                 typeAhead: false,
39584                 mode: 'local',
39585                 editable : false,
39586                 triggerAction: 'all',
39587                 emptyText:'Add tag',
39588                 selectOnFocus:true,
39589                 width:135,
39590                 listeners : {
39591                     'select': function(c, r, i) {
39592                         editor.insertTag(r.get('tag'));
39593                         editor.focus();
39594                     }
39595                 }
39596
39597             });
39598             tb.addField(this.formatCombo);
39599             
39600         }
39601         
39602         if(!this.disable.format){
39603             tb.add(
39604                 btn('bold'),
39605                 btn('italic'),
39606                 btn('underline')
39607             );
39608         };
39609         if(!this.disable.fontSize){
39610             tb.add(
39611                 '-',
39612                 
39613                 
39614                 btn('increasefontsize', false, editor.adjustFont),
39615                 btn('decreasefontsize', false, editor.adjustFont)
39616             );
39617         };
39618         
39619         
39620         if(this.disable.colors){
39621             tb.add(
39622                 '-', {
39623                     id:editor.frameId +'-forecolor',
39624                     cls:'x-btn-icon x-edit-forecolor',
39625                     clickEvent:'mousedown',
39626                     tooltip: this.buttonTips['forecolor'] || undefined,
39627                     tabIndex:-1,
39628                     menu : new Roo.menu.ColorMenu({
39629                         allowReselect: true,
39630                         focus: Roo.emptyFn,
39631                         value:'000000',
39632                         plain:true,
39633                         selectHandler: function(cp, color){
39634                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39635                             editor.deferFocus();
39636                         },
39637                         scope: editor,
39638                         clickEvent:'mousedown'
39639                     })
39640                 }, {
39641                     id:editor.frameId +'backcolor',
39642                     cls:'x-btn-icon x-edit-backcolor',
39643                     clickEvent:'mousedown',
39644                     tooltip: this.buttonTips['backcolor'] || undefined,
39645                     tabIndex:-1,
39646                     menu : new Roo.menu.ColorMenu({
39647                         focus: Roo.emptyFn,
39648                         value:'FFFFFF',
39649                         plain:true,
39650                         allowReselect: true,
39651                         selectHandler: function(cp, color){
39652                             if(Roo.isGecko){
39653                                 editor.execCmd('useCSS', false);
39654                                 editor.execCmd('hilitecolor', color);
39655                                 editor.execCmd('useCSS', true);
39656                                 editor.deferFocus();
39657                             }else{
39658                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39659                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39660                                 editor.deferFocus();
39661                             }
39662                         },
39663                         scope:editor,
39664                         clickEvent:'mousedown'
39665                     })
39666                 }
39667             );
39668         };
39669         // now add all the items...
39670         
39671
39672         if(!this.disable.alignments){
39673             tb.add(
39674                 '-',
39675                 btn('justifyleft'),
39676                 btn('justifycenter'),
39677                 btn('justifyright')
39678             );
39679         };
39680
39681         //if(!Roo.isSafari){
39682             if(!this.disable.links){
39683                 tb.add(
39684                     '-',
39685                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39686                 );
39687             };
39688
39689             if(!this.disable.lists){
39690                 tb.add(
39691                     '-',
39692                     btn('insertorderedlist'),
39693                     btn('insertunorderedlist')
39694                 );
39695             }
39696             if(!this.disable.sourceEdit){
39697                 tb.add(
39698                     '-',
39699                     btn('sourceedit', true, function(btn){
39700                         this.toggleSourceEdit(btn.pressed);
39701                     })
39702                 );
39703             }
39704         //}
39705         
39706         var smenu = { };
39707         // special menu.. - needs to be tidied up..
39708         if (!this.disable.special) {
39709             smenu = {
39710                 text: "&#169;",
39711                 cls: 'x-edit-none',
39712                 menu : {
39713                     items : []
39714                    }
39715             };
39716             for (var i =0; i < this.specialChars.length; i++) {
39717                 smenu.menu.items.push({
39718                     
39719                     html: this.specialChars[i],
39720                     handler: function(a,b) {
39721                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39722                         
39723                     },
39724                     tabIndex:-1
39725                 });
39726             }
39727             
39728             
39729             tb.add(smenu);
39730             
39731             
39732         }
39733         if (this.btns) {
39734             for(var i =0; i< this.btns.length;i++) {
39735                 var b = this.btns[i];
39736                 b.cls =  'x-edit-none';
39737                 b.scope = editor;
39738                 tb.add(b);
39739             }
39740         
39741         }
39742         
39743         
39744         
39745         // disable everything...
39746         
39747         this.tb.items.each(function(item){
39748            if(item.id != editor.frameId+ '-sourceedit'){
39749                 item.disable();
39750             }
39751         });
39752         this.rendered = true;
39753         
39754         // the all the btns;
39755         editor.on('editorevent', this.updateToolbar, this);
39756         // other toolbars need to implement this..
39757         //editor.on('editmodechange', this.updateToolbar, this);
39758     },
39759     
39760     
39761     
39762     /**
39763      * Protected method that will not generally be called directly. It triggers
39764      * a toolbar update by reading the markup state of the current selection in the editor.
39765      */
39766     updateToolbar: function(){
39767
39768         if(!this.editor.activated){
39769             this.editor.onFirstFocus();
39770             return;
39771         }
39772
39773         var btns = this.tb.items.map, 
39774             doc = this.editor.doc,
39775             frameId = this.editor.frameId;
39776
39777         if(!this.disable.font && !Roo.isSafari){
39778             /*
39779             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39780             if(name != this.fontSelect.dom.value){
39781                 this.fontSelect.dom.value = name;
39782             }
39783             */
39784         }
39785         if(!this.disable.format){
39786             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39787             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39788             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39789         }
39790         if(!this.disable.alignments){
39791             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39792             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39793             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39794         }
39795         if(!Roo.isSafari && !this.disable.lists){
39796             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39797             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39798         }
39799         
39800         var ans = this.editor.getAllAncestors();
39801         if (this.formatCombo) {
39802             
39803             
39804             var store = this.formatCombo.store;
39805             this.formatCombo.setValue("");
39806             for (var i =0; i < ans.length;i++) {
39807                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39808                     // select it..
39809                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39810                     break;
39811                 }
39812             }
39813         }
39814         
39815         
39816         
39817         // hides menus... - so this cant be on a menu...
39818         Roo.menu.MenuMgr.hideAll();
39819
39820         //this.editorsyncValue();
39821     },
39822    
39823     
39824     createFontOptions : function(){
39825         var buf = [], fs = this.fontFamilies, ff, lc;
39826         for(var i = 0, len = fs.length; i< len; i++){
39827             ff = fs[i];
39828             lc = ff.toLowerCase();
39829             buf.push(
39830                 '<option value="',lc,'" style="font-family:',ff,';"',
39831                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39832                     ff,
39833                 '</option>'
39834             );
39835         }
39836         return buf.join('');
39837     },
39838     
39839     toggleSourceEdit : function(sourceEditMode){
39840         if(sourceEditMode === undefined){
39841             sourceEditMode = !this.sourceEditMode;
39842         }
39843         this.sourceEditMode = sourceEditMode === true;
39844         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39845         // just toggle the button?
39846         if(btn.pressed !== this.editor.sourceEditMode){
39847             btn.toggle(this.editor.sourceEditMode);
39848             return;
39849         }
39850         
39851         if(this.sourceEditMode){
39852             this.tb.items.each(function(item){
39853                 if(item.cmd != 'sourceedit'){
39854                     item.disable();
39855                 }
39856             });
39857           
39858         }else{
39859             if(this.initialized){
39860                 this.tb.items.each(function(item){
39861                     item.enable();
39862                 });
39863             }
39864             
39865         }
39866         // tell the editor that it's been pressed..
39867         this.editor.toggleSourceEdit(sourceEditMode);
39868        
39869     },
39870      /**
39871      * Object collection of toolbar tooltips for the buttons in the editor. The key
39872      * is the command id associated with that button and the value is a valid QuickTips object.
39873      * For example:
39874 <pre><code>
39875 {
39876     bold : {
39877         title: 'Bold (Ctrl+B)',
39878         text: 'Make the selected text bold.',
39879         cls: 'x-html-editor-tip'
39880     },
39881     italic : {
39882         title: 'Italic (Ctrl+I)',
39883         text: 'Make the selected text italic.',
39884         cls: 'x-html-editor-tip'
39885     },
39886     ...
39887 </code></pre>
39888     * @type Object
39889      */
39890     buttonTips : {
39891         bold : {
39892             title: 'Bold (Ctrl+B)',
39893             text: 'Make the selected text bold.',
39894             cls: 'x-html-editor-tip'
39895         },
39896         italic : {
39897             title: 'Italic (Ctrl+I)',
39898             text: 'Make the selected text italic.',
39899             cls: 'x-html-editor-tip'
39900         },
39901         underline : {
39902             title: 'Underline (Ctrl+U)',
39903             text: 'Underline the selected text.',
39904             cls: 'x-html-editor-tip'
39905         },
39906         increasefontsize : {
39907             title: 'Grow Text',
39908             text: 'Increase the font size.',
39909             cls: 'x-html-editor-tip'
39910         },
39911         decreasefontsize : {
39912             title: 'Shrink Text',
39913             text: 'Decrease the font size.',
39914             cls: 'x-html-editor-tip'
39915         },
39916         backcolor : {
39917             title: 'Text Highlight Color',
39918             text: 'Change the background color of the selected text.',
39919             cls: 'x-html-editor-tip'
39920         },
39921         forecolor : {
39922             title: 'Font Color',
39923             text: 'Change the color of the selected text.',
39924             cls: 'x-html-editor-tip'
39925         },
39926         justifyleft : {
39927             title: 'Align Text Left',
39928             text: 'Align text to the left.',
39929             cls: 'x-html-editor-tip'
39930         },
39931         justifycenter : {
39932             title: 'Center Text',
39933             text: 'Center text in the editor.',
39934             cls: 'x-html-editor-tip'
39935         },
39936         justifyright : {
39937             title: 'Align Text Right',
39938             text: 'Align text to the right.',
39939             cls: 'x-html-editor-tip'
39940         },
39941         insertunorderedlist : {
39942             title: 'Bullet List',
39943             text: 'Start a bulleted list.',
39944             cls: 'x-html-editor-tip'
39945         },
39946         insertorderedlist : {
39947             title: 'Numbered List',
39948             text: 'Start a numbered list.',
39949             cls: 'x-html-editor-tip'
39950         },
39951         createlink : {
39952             title: 'Hyperlink',
39953             text: 'Make the selected text a hyperlink.',
39954             cls: 'x-html-editor-tip'
39955         },
39956         sourceedit : {
39957             title: 'Source Edit',
39958             text: 'Switch to source editing mode.',
39959             cls: 'x-html-editor-tip'
39960         }
39961     },
39962     // private
39963     onDestroy : function(){
39964         if(this.rendered){
39965             
39966             this.tb.items.each(function(item){
39967                 if(item.menu){
39968                     item.menu.removeAll();
39969                     if(item.menu.el){
39970                         item.menu.el.destroy();
39971                     }
39972                 }
39973                 item.destroy();
39974             });
39975              
39976         }
39977     },
39978     onFirstFocus: function() {
39979         this.tb.items.each(function(item){
39980            item.enable();
39981         });
39982     }
39983 });
39984
39985
39986
39987
39988 // <script type="text/javascript">
39989 /*
39990  * Based on
39991  * Ext JS Library 1.1.1
39992  * Copyright(c) 2006-2007, Ext JS, LLC.
39993  *  
39994  
39995  */
39996
39997  
39998 /**
39999  * @class Roo.form.HtmlEditor.ToolbarContext
40000  * Context Toolbar
40001  * 
40002  * Usage:
40003  *
40004  new Roo.form.HtmlEditor({
40005     ....
40006     toolbars : [
40007         new Roo.form.HtmlEditor.ToolbarStandard(),
40008         new Roo.form.HtmlEditor.ToolbarContext()
40009         })
40010     }
40011      
40012  * 
40013  * @config : {Object} disable List of elements to disable.. (not done yet.)
40014  * 
40015  * 
40016  */
40017
40018 Roo.form.HtmlEditor.ToolbarContext = function(config)
40019 {
40020     
40021     Roo.apply(this, config);
40022     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40023     // dont call parent... till later.
40024 }
40025 Roo.form.HtmlEditor.ToolbarContext.types = {
40026     'IMG' : {
40027         width : {
40028             title: "Width",
40029             width: 40
40030         },
40031         height:  {
40032             title: "Height",
40033             width: 40
40034         },
40035         align: {
40036             title: "Align",
40037             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40038             width : 80
40039             
40040         },
40041         border: {
40042             title: "Border",
40043             width: 40
40044         },
40045         alt: {
40046             title: "Alt",
40047             width: 120
40048         },
40049         src : {
40050             title: "Src",
40051             width: 220
40052         }
40053         
40054     },
40055     'A' : {
40056         name : {
40057             title: "Name",
40058             width: 50
40059         },
40060         href:  {
40061             title: "Href",
40062             width: 220
40063         } // border?
40064         
40065     },
40066     'TABLE' : {
40067         rows : {
40068             title: "Rows",
40069             width: 20
40070         },
40071         cols : {
40072             title: "Cols",
40073             width: 20
40074         },
40075         width : {
40076             title: "Width",
40077             width: 40
40078         },
40079         height : {
40080             title: "Height",
40081             width: 40
40082         },
40083         border : {
40084             title: "Border",
40085             width: 20
40086         }
40087     },
40088     'TD' : {
40089         width : {
40090             title: "Width",
40091             width: 40
40092         },
40093         height : {
40094             title: "Height",
40095             width: 40
40096         },   
40097         align: {
40098             title: "Align",
40099             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40100             width: 40
40101         },
40102         valign: {
40103             title: "Valign",
40104             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40105             width: 40
40106         },
40107         colspan: {
40108             title: "Colspan",
40109             width: 20
40110             
40111         }
40112     },
40113     'INPUT' : {
40114         name : {
40115             title: "name",
40116             width: 120
40117         },
40118         value : {
40119             title: "Value",
40120             width: 120
40121         },
40122         width : {
40123             title: "Width",
40124             width: 40
40125         }
40126     },
40127     'LABEL' : {
40128         'for' : {
40129             title: "For",
40130             width: 120
40131         }
40132     },
40133     'TEXTAREA' : {
40134           name : {
40135             title: "name",
40136             width: 120
40137         },
40138         rows : {
40139             title: "Rows",
40140             width: 20
40141         },
40142         cols : {
40143             title: "Cols",
40144             width: 20
40145         }
40146     },
40147     'SELECT' : {
40148         name : {
40149             title: "name",
40150             width: 120
40151         },
40152         selectoptions : {
40153             title: "Options",
40154             width: 200
40155         }
40156     },
40157     'BODY' : {
40158         title : {
40159             title: "title",
40160             width: 120,
40161             disabled : true
40162         }
40163     }
40164 };
40165
40166
40167
40168 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40169     
40170     tb: false,
40171     
40172     rendered: false,
40173     
40174     editor : false,
40175     /**
40176      * @cfg {Object} disable  List of toolbar elements to disable
40177          
40178      */
40179     disable : false,
40180     
40181     
40182     
40183     toolbars : false,
40184     
40185     init : function(editor)
40186     {
40187         this.editor = editor;
40188         
40189         
40190         var fid = editor.frameId;
40191         var etb = this;
40192         function btn(id, toggle, handler){
40193             var xid = fid + '-'+ id ;
40194             return {
40195                 id : xid,
40196                 cmd : id,
40197                 cls : 'x-btn-icon x-edit-'+id,
40198                 enableToggle:toggle !== false,
40199                 scope: editor, // was editor...
40200                 handler:handler||editor.relayBtnCmd,
40201                 clickEvent:'mousedown',
40202                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40203                 tabIndex:-1
40204             };
40205         }
40206         // create a new element.
40207         var wdiv = editor.wrap.createChild({
40208                 tag: 'div'
40209             }, editor.wrap.dom.firstChild.nextSibling, true);
40210         
40211         // can we do this more than once??
40212         
40213          // stop form submits
40214       
40215  
40216         // disable everything...
40217         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40218         this.toolbars = {};
40219            
40220         for (var i in  ty) {
40221           
40222             this.toolbars[i] = this.buildToolbar(ty[i],i);
40223         }
40224         this.tb = this.toolbars.BODY;
40225         this.tb.el.show();
40226         
40227          
40228         this.rendered = true;
40229         
40230         // the all the btns;
40231         editor.on('editorevent', this.updateToolbar, this);
40232         // other toolbars need to implement this..
40233         //editor.on('editmodechange', this.updateToolbar, this);
40234     },
40235     
40236     
40237     
40238     /**
40239      * Protected method that will not generally be called directly. It triggers
40240      * a toolbar update by reading the markup state of the current selection in the editor.
40241      */
40242     updateToolbar: function(){
40243
40244         if(!this.editor.activated){
40245             this.editor.onFirstFocus();
40246             return;
40247         }
40248
40249         
40250         var ans = this.editor.getAllAncestors();
40251         
40252         // pick
40253         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40254         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40255         sel = sel ? sel : this.editor.doc.body;
40256         sel = sel.tagName.length ? sel : this.editor.doc.body;
40257         var tn = sel.tagName.toUpperCase();
40258         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40259         tn = sel.tagName.toUpperCase();
40260         if (this.tb.name  == tn) {
40261             return; // no change
40262         }
40263         this.tb.el.hide();
40264         ///console.log("show: " + tn);
40265         this.tb =  this.toolbars[tn];
40266         this.tb.el.show();
40267         this.tb.fields.each(function(e) {
40268             e.setValue(sel.getAttribute(e.name));
40269         });
40270         this.tb.selectedNode = sel;
40271         
40272         
40273         Roo.menu.MenuMgr.hideAll();
40274
40275         //this.editorsyncValue();
40276     },
40277    
40278        
40279     // private
40280     onDestroy : function(){
40281         if(this.rendered){
40282             
40283             this.tb.items.each(function(item){
40284                 if(item.menu){
40285                     item.menu.removeAll();
40286                     if(item.menu.el){
40287                         item.menu.el.destroy();
40288                     }
40289                 }
40290                 item.destroy();
40291             });
40292              
40293         }
40294     },
40295     onFirstFocus: function() {
40296         // need to do this for all the toolbars..
40297         this.tb.items.each(function(item){
40298            item.enable();
40299         });
40300     },
40301     buildToolbar: function(tlist, nm)
40302     {
40303         var editor = this.editor;
40304          // create a new element.
40305         var wdiv = editor.wrap.createChild({
40306                 tag: 'div'
40307             }, editor.wrap.dom.firstChild.nextSibling, true);
40308         
40309        
40310         var tb = new Roo.Toolbar(wdiv);
40311         tb.add(nm+ ":&nbsp;");
40312         for (var i in tlist) {
40313             var item = tlist[i];
40314             tb.add(item.title + ":&nbsp;");
40315             if (item.opts) {
40316                 // fixme
40317                 
40318               
40319                 tb.addField( new Roo.form.ComboBox({
40320                     store: new Roo.data.SimpleStore({
40321                         id : 'val',
40322                         fields: ['val'],
40323                         data : item.opts // from states.js
40324                     }),
40325                     name : i,
40326                     displayField:'val',
40327                     typeAhead: false,
40328                     mode: 'local',
40329                     editable : false,
40330                     triggerAction: 'all',
40331                     emptyText:'Select',
40332                     selectOnFocus:true,
40333                     width: item.width ? item.width  : 130,
40334                     listeners : {
40335                         'select': function(c, r, i) {
40336                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40337                         }
40338                     }
40339
40340                 }));
40341                 continue;
40342                     
40343                 
40344                 
40345                 
40346                 
40347                 tb.addField( new Roo.form.TextField({
40348                     name: i,
40349                     width: 100,
40350                     //allowBlank:false,
40351                     value: ''
40352                 }));
40353                 continue;
40354             }
40355             tb.addField( new Roo.form.TextField({
40356                 name: i,
40357                 width: item.width,
40358                 //allowBlank:true,
40359                 value: '',
40360                 listeners: {
40361                     'change' : function(f, nv, ov) {
40362                         tb.selectedNode.setAttribute(f.name, nv);
40363                     }
40364                 }
40365             }));
40366              
40367         }
40368         tb.el.on('click', function(e){
40369             e.preventDefault(); // what does this do?
40370         });
40371         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40372         tb.el.hide();
40373         tb.name = nm;
40374         // dont need to disable them... as they will get hidden
40375         return tb;
40376          
40377         
40378     }
40379     
40380     
40381     
40382     
40383 });
40384
40385
40386
40387
40388
40389 /*
40390  * Based on:
40391  * Ext JS Library 1.1.1
40392  * Copyright(c) 2006-2007, Ext JS, LLC.
40393  *
40394  * Originally Released Under LGPL - original licence link has changed is not relivant.
40395  *
40396  * Fork - LGPL
40397  * <script type="text/javascript">
40398  */
40399  
40400 /**
40401  * @class Roo.form.BasicForm
40402  * @extends Roo.util.Observable
40403  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40404  * @constructor
40405  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40406  * @param {Object} config Configuration options
40407  */
40408 Roo.form.BasicForm = function(el, config){
40409     this.allItems = [];
40410     this.childForms = [];
40411     Roo.apply(this, config);
40412     /*
40413      * The Roo.form.Field items in this form.
40414      * @type MixedCollection
40415      */
40416      
40417      
40418     this.items = new Roo.util.MixedCollection(false, function(o){
40419         return o.id || (o.id = Roo.id());
40420     });
40421     this.addEvents({
40422         /**
40423          * @event beforeaction
40424          * Fires before any action is performed. Return false to cancel the action.
40425          * @param {Form} this
40426          * @param {Action} action The action to be performed
40427          */
40428         beforeaction: true,
40429         /**
40430          * @event actionfailed
40431          * Fires when an action fails.
40432          * @param {Form} this
40433          * @param {Action} action The action that failed
40434          */
40435         actionfailed : true,
40436         /**
40437          * @event actioncomplete
40438          * Fires when an action is completed.
40439          * @param {Form} this
40440          * @param {Action} action The action that completed
40441          */
40442         actioncomplete : true
40443     });
40444     if(el){
40445         this.initEl(el);
40446     }
40447     Roo.form.BasicForm.superclass.constructor.call(this);
40448 };
40449
40450 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40451     /**
40452      * @cfg {String} method
40453      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40454      */
40455     /**
40456      * @cfg {DataReader} reader
40457      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40458      * This is optional as there is built-in support for processing JSON.
40459      */
40460     /**
40461      * @cfg {DataReader} errorReader
40462      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40463      * This is completely optional as there is built-in support for processing JSON.
40464      */
40465     /**
40466      * @cfg {String} url
40467      * The URL to use for form actions if one isn't supplied in the action options.
40468      */
40469     /**
40470      * @cfg {Boolean} fileUpload
40471      * Set to true if this form is a file upload.
40472      */
40473      
40474     /**
40475      * @cfg {Object} baseParams
40476      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40477      */
40478      /**
40479      
40480     /**
40481      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40482      */
40483     timeout: 30,
40484
40485     // private
40486     activeAction : null,
40487
40488     /**
40489      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40490      * or setValues() data instead of when the form was first created.
40491      */
40492     trackResetOnLoad : false,
40493     
40494     
40495     /**
40496      * childForms - used for multi-tab forms
40497      * @type {Array}
40498      */
40499     childForms : false,
40500     
40501     /**
40502      * allItems - full list of fields.
40503      * @type {Array}
40504      */
40505     allItems : false,
40506     
40507     /**
40508      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40509      * element by passing it or its id or mask the form itself by passing in true.
40510      * @type Mixed
40511      */
40512     waitMsgTarget : false,
40513
40514     // private
40515     initEl : function(el){
40516         this.el = Roo.get(el);
40517         this.id = this.el.id || Roo.id();
40518         this.el.on('submit', this.onSubmit, this);
40519         this.el.addClass('x-form');
40520     },
40521
40522     // private
40523     onSubmit : function(e){
40524         e.stopEvent();
40525     },
40526
40527     /**
40528      * Returns true if client-side validation on the form is successful.
40529      * @return Boolean
40530      */
40531     isValid : function(){
40532         var valid = true;
40533         this.items.each(function(f){
40534            if(!f.validate()){
40535                valid = false;
40536            }
40537         });
40538         return valid;
40539     },
40540
40541     /**
40542      * Returns true if any fields in this form have changed since their original load.
40543      * @return Boolean
40544      */
40545     isDirty : function(){
40546         var dirty = false;
40547         this.items.each(function(f){
40548            if(f.isDirty()){
40549                dirty = true;
40550                return false;
40551            }
40552         });
40553         return dirty;
40554     },
40555
40556     /**
40557      * Performs a predefined action (submit or load) or custom actions you define on this form.
40558      * @param {String} actionName The name of the action type
40559      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40560      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40561      * accept other config options):
40562      * <pre>
40563 Property          Type             Description
40564 ----------------  ---------------  ----------------------------------------------------------------------------------
40565 url               String           The url for the action (defaults to the form's url)
40566 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40567 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40568 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40569                                    validate the form on the client (defaults to false)
40570      * </pre>
40571      * @return {BasicForm} this
40572      */
40573     doAction : function(action, options){
40574         if(typeof action == 'string'){
40575             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40576         }
40577         if(this.fireEvent('beforeaction', this, action) !== false){
40578             this.beforeAction(action);
40579             action.run.defer(100, action);
40580         }
40581         return this;
40582     },
40583
40584     /**
40585      * Shortcut to do a submit action.
40586      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40587      * @return {BasicForm} this
40588      */
40589     submit : function(options){
40590         this.doAction('submit', options);
40591         return this;
40592     },
40593
40594     /**
40595      * Shortcut to do a load action.
40596      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40597      * @return {BasicForm} this
40598      */
40599     load : function(options){
40600         this.doAction('load', options);
40601         return this;
40602     },
40603
40604     /**
40605      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40606      * @param {Record} record The record to edit
40607      * @return {BasicForm} this
40608      */
40609     updateRecord : function(record){
40610         record.beginEdit();
40611         var fs = record.fields;
40612         fs.each(function(f){
40613             var field = this.findField(f.name);
40614             if(field){
40615                 record.set(f.name, field.getValue());
40616             }
40617         }, this);
40618         record.endEdit();
40619         return this;
40620     },
40621
40622     /**
40623      * Loads an Roo.data.Record into this form.
40624      * @param {Record} record The record to load
40625      * @return {BasicForm} this
40626      */
40627     loadRecord : function(record){
40628         this.setValues(record.data);
40629         return this;
40630     },
40631
40632     // private
40633     beforeAction : function(action){
40634         var o = action.options;
40635         
40636        
40637         if(this.waitMsgTarget === true){
40638             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40639         }else if(this.waitMsgTarget){
40640             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40641             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40642         }else {
40643             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40644         }
40645          
40646     },
40647
40648     // private
40649     afterAction : function(action, success){
40650         this.activeAction = null;
40651         var o = action.options;
40652         
40653         if(this.waitMsgTarget === true){
40654             this.el.unmask();
40655         }else if(this.waitMsgTarget){
40656             this.waitMsgTarget.unmask();
40657         }else{
40658             Roo.MessageBox.updateProgress(1);
40659             Roo.MessageBox.hide();
40660         }
40661          
40662         if(success){
40663             if(o.reset){
40664                 this.reset();
40665             }
40666             Roo.callback(o.success, o.scope, [this, action]);
40667             this.fireEvent('actioncomplete', this, action);
40668             
40669         }else{
40670             Roo.callback(o.failure, o.scope, [this, action]);
40671             // show an error message if no failed handler is set..
40672             if (!this.hasListener('actionfailed')) {
40673                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40674             }
40675             
40676             this.fireEvent('actionfailed', this, action);
40677         }
40678         
40679     },
40680
40681     /**
40682      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40683      * @param {String} id The value to search for
40684      * @return Field
40685      */
40686     findField : function(id){
40687         var field = this.items.get(id);
40688         if(!field){
40689             this.items.each(function(f){
40690                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40691                     field = f;
40692                     return false;
40693                 }
40694             });
40695         }
40696         return field || null;
40697     },
40698
40699     /**
40700      * Add a secondary form to this one, 
40701      * Used to provide tabbed forms. One form is primary, with hidden values 
40702      * which mirror the elements from the other forms.
40703      * 
40704      * @param {Roo.form.Form} form to add.
40705      * 
40706      */
40707     addForm : function(form)
40708     {
40709        
40710         if (this.childForms.indexOf(form) > -1) {
40711             // already added..
40712             return;
40713         }
40714         this.childForms.push(form);
40715         var n = '';
40716         Roo.each(form.allItems, function (fe) {
40717             
40718             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40719             if (this.findField(n)) { // already added..
40720                 return;
40721             }
40722             var add = new Roo.form.Hidden({
40723                 name : n
40724             });
40725             add.render(this.el);
40726             
40727             this.add( add );
40728         }, this);
40729         
40730     },
40731     /**
40732      * Mark fields in this form invalid in bulk.
40733      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40734      * @return {BasicForm} this
40735      */
40736     markInvalid : function(errors){
40737         if(errors instanceof Array){
40738             for(var i = 0, len = errors.length; i < len; i++){
40739                 var fieldError = errors[i];
40740                 var f = this.findField(fieldError.id);
40741                 if(f){
40742                     f.markInvalid(fieldError.msg);
40743                 }
40744             }
40745         }else{
40746             var field, id;
40747             for(id in errors){
40748                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40749                     field.markInvalid(errors[id]);
40750                 }
40751             }
40752         }
40753         Roo.each(this.childForms || [], function (f) {
40754             f.markInvalid(errors);
40755         });
40756         
40757         return this;
40758     },
40759
40760     /**
40761      * Set values for fields in this form in bulk.
40762      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40763      * @return {BasicForm} this
40764      */
40765     setValues : function(values){
40766         if(values instanceof Array){ // array of objects
40767             for(var i = 0, len = values.length; i < len; i++){
40768                 var v = values[i];
40769                 var f = this.findField(v.id);
40770                 if(f){
40771                     f.setValue(v.value);
40772                     if(this.trackResetOnLoad){
40773                         f.originalValue = f.getValue();
40774                     }
40775                 }
40776             }
40777         }else{ // object hash
40778             var field, id;
40779             for(id in values){
40780                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40781                     
40782                     if (field.setFromData && 
40783                         field.valueField && 
40784                         field.displayField &&
40785                         // combos' with local stores can 
40786                         // be queried via setValue()
40787                         // to set their value..
40788                         (field.store && !field.store.isLocal)
40789                         ) {
40790                         // it's a combo
40791                         var sd = { };
40792                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40793                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40794                         field.setFromData(sd);
40795                         
40796                     } else {
40797                         field.setValue(values[id]);
40798                     }
40799                     
40800                     
40801                     if(this.trackResetOnLoad){
40802                         field.originalValue = field.getValue();
40803                     }
40804                 }
40805             }
40806         }
40807          
40808         Roo.each(this.childForms || [], function (f) {
40809             f.setValues(values);
40810         });
40811                 
40812         return this;
40813     },
40814
40815     /**
40816      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40817      * they are returned as an array.
40818      * @param {Boolean} asString
40819      * @return {Object}
40820      */
40821     getValues : function(asString){
40822         if (this.childForms) {
40823             // copy values from the child forms
40824             Roo.each(this.childForms, function (f) {
40825                 this.setValues(f.getValues());
40826             }, this);
40827         }
40828         
40829         
40830         
40831         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40832         if(asString === true){
40833             return fs;
40834         }
40835         return Roo.urlDecode(fs);
40836     },
40837     
40838     /**
40839      * Returns the fields in this form as an object with key/value pairs. 
40840      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40841      * @return {Object}
40842      */
40843     getFieldValues : function()
40844     {
40845         if (this.childForms) {
40846             // copy values from the child forms
40847             Roo.each(this.childForms, function (f) {
40848                 this.setValues(f.getValues());
40849             }, this);
40850         }
40851         
40852         var ret = {};
40853         this.items.each(function(f){
40854             if (!f.getName()) {
40855                 return;
40856             }
40857             var v = f.getValue();
40858             if ((typeof(v) == 'object') && f.getRawValue) {
40859                 v = f.getRawValue() ; // dates..
40860             }
40861             ret[f.getName()] = v;
40862         });
40863         
40864         return ret;
40865     },
40866
40867     /**
40868      * Clears all invalid messages in this form.
40869      * @return {BasicForm} this
40870      */
40871     clearInvalid : function(){
40872         this.items.each(function(f){
40873            f.clearInvalid();
40874         });
40875         
40876         Roo.each(this.childForms || [], function (f) {
40877             f.clearInvalid();
40878         });
40879         
40880         
40881         return this;
40882     },
40883
40884     /**
40885      * Resets this form.
40886      * @return {BasicForm} this
40887      */
40888     reset : function(){
40889         this.items.each(function(f){
40890             f.reset();
40891         });
40892         
40893         Roo.each(this.childForms || [], function (f) {
40894             f.reset();
40895         });
40896        
40897         
40898         return this;
40899     },
40900
40901     /**
40902      * Add Roo.form components to this form.
40903      * @param {Field} field1
40904      * @param {Field} field2 (optional)
40905      * @param {Field} etc (optional)
40906      * @return {BasicForm} this
40907      */
40908     add : function(){
40909         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40910         return this;
40911     },
40912
40913
40914     /**
40915      * Removes a field from the items collection (does NOT remove its markup).
40916      * @param {Field} field
40917      * @return {BasicForm} this
40918      */
40919     remove : function(field){
40920         this.items.remove(field);
40921         return this;
40922     },
40923
40924     /**
40925      * Looks at the fields in this form, checks them for an id attribute,
40926      * and calls applyTo on the existing dom element with that id.
40927      * @return {BasicForm} this
40928      */
40929     render : function(){
40930         this.items.each(function(f){
40931             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40932                 f.applyTo(f.id);
40933             }
40934         });
40935         return this;
40936     },
40937
40938     /**
40939      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40940      * @param {Object} values
40941      * @return {BasicForm} this
40942      */
40943     applyToFields : function(o){
40944         this.items.each(function(f){
40945            Roo.apply(f, o);
40946         });
40947         return this;
40948     },
40949
40950     /**
40951      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40952      * @param {Object} values
40953      * @return {BasicForm} this
40954      */
40955     applyIfToFields : function(o){
40956         this.items.each(function(f){
40957            Roo.applyIf(f, o);
40958         });
40959         return this;
40960     }
40961 });
40962
40963 // back compat
40964 Roo.BasicForm = Roo.form.BasicForm;/*
40965  * Based on:
40966  * Ext JS Library 1.1.1
40967  * Copyright(c) 2006-2007, Ext JS, LLC.
40968  *
40969  * Originally Released Under LGPL - original licence link has changed is not relivant.
40970  *
40971  * Fork - LGPL
40972  * <script type="text/javascript">
40973  */
40974
40975 /**
40976  * @class Roo.form.Form
40977  * @extends Roo.form.BasicForm
40978  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40979  * @constructor
40980  * @param {Object} config Configuration options
40981  */
40982 Roo.form.Form = function(config){
40983     var xitems =  [];
40984     if (config.items) {
40985         xitems = config.items;
40986         delete config.items;
40987     }
40988    
40989     
40990     Roo.form.Form.superclass.constructor.call(this, null, config);
40991     this.url = this.url || this.action;
40992     if(!this.root){
40993         this.root = new Roo.form.Layout(Roo.applyIf({
40994             id: Roo.id()
40995         }, config));
40996     }
40997     this.active = this.root;
40998     /**
40999      * Array of all the buttons that have been added to this form via {@link addButton}
41000      * @type Array
41001      */
41002     this.buttons = [];
41003     this.allItems = [];
41004     this.addEvents({
41005         /**
41006          * @event clientvalidation
41007          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41008          * @param {Form} this
41009          * @param {Boolean} valid true if the form has passed client-side validation
41010          */
41011         clientvalidation: true,
41012         /**
41013          * @event rendered
41014          * Fires when the form is rendered
41015          * @param {Roo.form.Form} form
41016          */
41017         rendered : true
41018     });
41019     
41020     if (this.progressUrl) {
41021             // push a hidden field onto the list of fields..
41022             this.addxtype( {
41023                     xns: Roo.form, 
41024                     xtype : 'Hidden', 
41025                     name : 'UPLOAD_IDENTIFIER' 
41026             });
41027         }
41028         
41029     
41030     Roo.each(xitems, this.addxtype, this);
41031     
41032     
41033     
41034 };
41035
41036 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41037     /**
41038      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41039      */
41040     /**
41041      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41042      */
41043     /**
41044      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41045      */
41046     buttonAlign:'center',
41047
41048     /**
41049      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41050      */
41051     minButtonWidth:75,
41052
41053     /**
41054      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41055      * This property cascades to child containers if not set.
41056      */
41057     labelAlign:'left',
41058
41059     /**
41060      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41061      * fires a looping event with that state. This is required to bind buttons to the valid
41062      * state using the config value formBind:true on the button.
41063      */
41064     monitorValid : false,
41065
41066     /**
41067      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41068      */
41069     monitorPoll : 200,
41070     
41071     /**
41072      * @cfg {String} progressUrl - Url to return progress data 
41073      */
41074     
41075     progressUrl : false,
41076   
41077     /**
41078      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41079      * fields are added and the column is closed. If no fields are passed the column remains open
41080      * until end() is called.
41081      * @param {Object} config The config to pass to the column
41082      * @param {Field} field1 (optional)
41083      * @param {Field} field2 (optional)
41084      * @param {Field} etc (optional)
41085      * @return Column The column container object
41086      */
41087     column : function(c){
41088         var col = new Roo.form.Column(c);
41089         this.start(col);
41090         if(arguments.length > 1){ // duplicate code required because of Opera
41091             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41092             this.end();
41093         }
41094         return col;
41095     },
41096
41097     /**
41098      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41099      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41100      * until end() is called.
41101      * @param {Object} config The config to pass to the fieldset
41102      * @param {Field} field1 (optional)
41103      * @param {Field} field2 (optional)
41104      * @param {Field} etc (optional)
41105      * @return FieldSet The fieldset container object
41106      */
41107     fieldset : function(c){
41108         var fs = new Roo.form.FieldSet(c);
41109         this.start(fs);
41110         if(arguments.length > 1){ // duplicate code required because of Opera
41111             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41112             this.end();
41113         }
41114         return fs;
41115     },
41116
41117     /**
41118      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41119      * fields are added and the container is closed. If no fields are passed the container remains open
41120      * until end() is called.
41121      * @param {Object} config The config to pass to the Layout
41122      * @param {Field} field1 (optional)
41123      * @param {Field} field2 (optional)
41124      * @param {Field} etc (optional)
41125      * @return Layout The container object
41126      */
41127     container : function(c){
41128         var l = new Roo.form.Layout(c);
41129         this.start(l);
41130         if(arguments.length > 1){ // duplicate code required because of Opera
41131             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41132             this.end();
41133         }
41134         return l;
41135     },
41136
41137     /**
41138      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41139      * @param {Object} container A Roo.form.Layout or subclass of Layout
41140      * @return {Form} this
41141      */
41142     start : function(c){
41143         // cascade label info
41144         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41145         this.active.stack.push(c);
41146         c.ownerCt = this.active;
41147         this.active = c;
41148         return this;
41149     },
41150
41151     /**
41152      * Closes the current open container
41153      * @return {Form} this
41154      */
41155     end : function(){
41156         if(this.active == this.root){
41157             return this;
41158         }
41159         this.active = this.active.ownerCt;
41160         return this;
41161     },
41162
41163     /**
41164      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41165      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41166      * as the label of the field.
41167      * @param {Field} field1
41168      * @param {Field} field2 (optional)
41169      * @param {Field} etc. (optional)
41170      * @return {Form} this
41171      */
41172     add : function(){
41173         this.active.stack.push.apply(this.active.stack, arguments);
41174         this.allItems.push.apply(this.allItems,arguments);
41175         var r = [];
41176         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41177             if(a[i].isFormField){
41178                 r.push(a[i]);
41179             }
41180         }
41181         if(r.length > 0){
41182             Roo.form.Form.superclass.add.apply(this, r);
41183         }
41184         return this;
41185     },
41186     
41187
41188     
41189     
41190     
41191      /**
41192      * Find any element that has been added to a form, using it's ID or name
41193      * This can include framesets, columns etc. along with regular fields..
41194      * @param {String} id - id or name to find.
41195      
41196      * @return {Element} e - or false if nothing found.
41197      */
41198     findbyId : function(id)
41199     {
41200         var ret = false;
41201         if (!id) {
41202             return ret;
41203         }
41204         Roo.each(this.allItems, function(f){
41205             if (f.id == id || f.name == id ){
41206                 ret = f;
41207                 return false;
41208             }
41209         });
41210         return ret;
41211     },
41212
41213     
41214     
41215     /**
41216      * Render this form into the passed container. This should only be called once!
41217      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41218      * @return {Form} this
41219      */
41220     render : function(ct)
41221     {
41222         
41223         
41224         
41225         ct = Roo.get(ct);
41226         var o = this.autoCreate || {
41227             tag: 'form',
41228             method : this.method || 'POST',
41229             id : this.id || Roo.id()
41230         };
41231         this.initEl(ct.createChild(o));
41232
41233         this.root.render(this.el);
41234         
41235        
41236              
41237         this.items.each(function(f){
41238             f.render('x-form-el-'+f.id);
41239         });
41240
41241         if(this.buttons.length > 0){
41242             // tables are required to maintain order and for correct IE layout
41243             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41244                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41245                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41246             }}, null, true);
41247             var tr = tb.getElementsByTagName('tr')[0];
41248             for(var i = 0, len = this.buttons.length; i < len; i++) {
41249                 var b = this.buttons[i];
41250                 var td = document.createElement('td');
41251                 td.className = 'x-form-btn-td';
41252                 b.render(tr.appendChild(td));
41253             }
41254         }
41255         if(this.monitorValid){ // initialize after render
41256             this.startMonitoring();
41257         }
41258         this.fireEvent('rendered', this);
41259         return this;
41260     },
41261
41262     /**
41263      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41264      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41265      * object or a valid Roo.DomHelper element config
41266      * @param {Function} handler The function called when the button is clicked
41267      * @param {Object} scope (optional) The scope of the handler function
41268      * @return {Roo.Button}
41269      */
41270     addButton : function(config, handler, scope){
41271         var bc = {
41272             handler: handler,
41273             scope: scope,
41274             minWidth: this.minButtonWidth,
41275             hideParent:true
41276         };
41277         if(typeof config == "string"){
41278             bc.text = config;
41279         }else{
41280             Roo.apply(bc, config);
41281         }
41282         var btn = new Roo.Button(null, bc);
41283         this.buttons.push(btn);
41284         return btn;
41285     },
41286
41287      /**
41288      * Adds a series of form elements (using the xtype property as the factory method.
41289      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41290      * @param {Object} config 
41291      */
41292     
41293     addxtype : function()
41294     {
41295         var ar = Array.prototype.slice.call(arguments, 0);
41296         var ret = false;
41297         for(var i = 0; i < ar.length; i++) {
41298             if (!ar[i]) {
41299                 continue; // skip -- if this happends something invalid got sent, we 
41300                 // should ignore it, as basically that interface element will not show up
41301                 // and that should be pretty obvious!!
41302             }
41303             
41304             if (Roo.form[ar[i].xtype]) {
41305                 ar[i].form = this;
41306                 var fe = Roo.factory(ar[i], Roo.form);
41307                 if (!ret) {
41308                     ret = fe;
41309                 }
41310                 fe.form = this;
41311                 if (fe.store) {
41312                     fe.store.form = this;
41313                 }
41314                 if (fe.isLayout) {  
41315                          
41316                     this.start(fe);
41317                     this.allItems.push(fe);
41318                     if (fe.items && fe.addxtype) {
41319                         fe.addxtype.apply(fe, fe.items);
41320                         delete fe.items;
41321                     }
41322                      this.end();
41323                     continue;
41324                 }
41325                 
41326                 
41327                  
41328                 this.add(fe);
41329               //  console.log('adding ' + ar[i].xtype);
41330             }
41331             if (ar[i].xtype == 'Button') {  
41332                 //console.log('adding button');
41333                 //console.log(ar[i]);
41334                 this.addButton(ar[i]);
41335                 this.allItems.push(fe);
41336                 continue;
41337             }
41338             
41339             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41340                 alert('end is not supported on xtype any more, use items');
41341             //    this.end();
41342             //    //console.log('adding end');
41343             }
41344             
41345         }
41346         return ret;
41347     },
41348     
41349     /**
41350      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41351      * option "monitorValid"
41352      */
41353     startMonitoring : function(){
41354         if(!this.bound){
41355             this.bound = true;
41356             Roo.TaskMgr.start({
41357                 run : this.bindHandler,
41358                 interval : this.monitorPoll || 200,
41359                 scope: this
41360             });
41361         }
41362     },
41363
41364     /**
41365      * Stops monitoring of the valid state of this form
41366      */
41367     stopMonitoring : function(){
41368         this.bound = false;
41369     },
41370
41371     // private
41372     bindHandler : function(){
41373         if(!this.bound){
41374             return false; // stops binding
41375         }
41376         var valid = true;
41377         this.items.each(function(f){
41378             if(!f.isValid(true)){
41379                 valid = false;
41380                 return false;
41381             }
41382         });
41383         for(var i = 0, len = this.buttons.length; i < len; i++){
41384             var btn = this.buttons[i];
41385             if(btn.formBind === true && btn.disabled === valid){
41386                 btn.setDisabled(!valid);
41387             }
41388         }
41389         this.fireEvent('clientvalidation', this, valid);
41390     }
41391     
41392     
41393     
41394     
41395     
41396     
41397     
41398     
41399 });
41400
41401
41402 // back compat
41403 Roo.Form = Roo.form.Form;
41404 /*
41405  * Based on:
41406  * Ext JS Library 1.1.1
41407  * Copyright(c) 2006-2007, Ext JS, LLC.
41408  *
41409  * Originally Released Under LGPL - original licence link has changed is not relivant.
41410  *
41411  * Fork - LGPL
41412  * <script type="text/javascript">
41413  */
41414  
41415  /**
41416  * @class Roo.form.Action
41417  * Internal Class used to handle form actions
41418  * @constructor
41419  * @param {Roo.form.BasicForm} el The form element or its id
41420  * @param {Object} config Configuration options
41421  */
41422  
41423  
41424 // define the action interface
41425 Roo.form.Action = function(form, options){
41426     this.form = form;
41427     this.options = options || {};
41428 };
41429 /**
41430  * Client Validation Failed
41431  * @const 
41432  */
41433 Roo.form.Action.CLIENT_INVALID = 'client';
41434 /**
41435  * Server Validation Failed
41436  * @const 
41437  */
41438  Roo.form.Action.SERVER_INVALID = 'server';
41439  /**
41440  * Connect to Server Failed
41441  * @const 
41442  */
41443 Roo.form.Action.CONNECT_FAILURE = 'connect';
41444 /**
41445  * Reading Data from Server Failed
41446  * @const 
41447  */
41448 Roo.form.Action.LOAD_FAILURE = 'load';
41449
41450 Roo.form.Action.prototype = {
41451     type : 'default',
41452     failureType : undefined,
41453     response : undefined,
41454     result : undefined,
41455
41456     // interface method
41457     run : function(options){
41458
41459     },
41460
41461     // interface method
41462     success : function(response){
41463
41464     },
41465
41466     // interface method
41467     handleResponse : function(response){
41468
41469     },
41470
41471     // default connection failure
41472     failure : function(response){
41473         
41474         this.response = response;
41475         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41476         this.form.afterAction(this, false);
41477     },
41478
41479     processResponse : function(response){
41480         this.response = response;
41481         if(!response.responseText){
41482             return true;
41483         }
41484         this.result = this.handleResponse(response);
41485         return this.result;
41486     },
41487
41488     // utility functions used internally
41489     getUrl : function(appendParams){
41490         var url = this.options.url || this.form.url || this.form.el.dom.action;
41491         if(appendParams){
41492             var p = this.getParams();
41493             if(p){
41494                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41495             }
41496         }
41497         return url;
41498     },
41499
41500     getMethod : function(){
41501         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41502     },
41503
41504     getParams : function(){
41505         var bp = this.form.baseParams;
41506         var p = this.options.params;
41507         if(p){
41508             if(typeof p == "object"){
41509                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41510             }else if(typeof p == 'string' && bp){
41511                 p += '&' + Roo.urlEncode(bp);
41512             }
41513         }else if(bp){
41514             p = Roo.urlEncode(bp);
41515         }
41516         return p;
41517     },
41518
41519     createCallback : function(){
41520         return {
41521             success: this.success,
41522             failure: this.failure,
41523             scope: this,
41524             timeout: (this.form.timeout*1000),
41525             upload: this.form.fileUpload ? this.success : undefined
41526         };
41527     }
41528 };
41529
41530 Roo.form.Action.Submit = function(form, options){
41531     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41532 };
41533
41534 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41535     type : 'submit',
41536
41537     haveProgress : false,
41538     uploadComplete : false,
41539     
41540     // uploadProgress indicator.
41541     uploadProgress : function()
41542     {
41543         if (!this.form.progressUrl) {
41544             return;
41545         }
41546         
41547         if (!this.haveProgress) {
41548             Roo.MessageBox.progress("Uploading", "Uploading");
41549         }
41550         if (this.uploadComplete) {
41551            Roo.MessageBox.hide();
41552            return;
41553         }
41554         
41555         this.haveProgress = true;
41556    
41557         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41558         
41559         var c = new Roo.data.Connection();
41560         c.request({
41561             url : this.form.progressUrl,
41562             params: {
41563                 id : uid
41564             },
41565             method: 'GET',
41566             success : function(req){
41567                //console.log(data);
41568                 var rdata = false;
41569                 var edata;
41570                 try  {
41571                    rdata = Roo.decode(req.responseText)
41572                 } catch (e) {
41573                     Roo.log("Invalid data from server..");
41574                     Roo.log(edata);
41575                     return;
41576                 }
41577                 if (!rdata || !rdata.success) {
41578                     Roo.log(rdata);
41579                     return;
41580                 }
41581                 var data = rdata.data;
41582                 
41583                 if (this.uploadComplete) {
41584                    Roo.MessageBox.hide();
41585                    return;
41586                 }
41587                    
41588                 if (data){
41589                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41590                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41591                     );
41592                 }
41593                 this.uploadProgress.defer(2000,this);
41594             },
41595        
41596             failure: function(data) {
41597                 Roo.log('progress url failed ');
41598                 Roo.log(data);
41599             },
41600             scope : this
41601         });
41602            
41603     },
41604     
41605     
41606     run : function()
41607     {
41608         // run get Values on the form, so it syncs any secondary forms.
41609         this.form.getValues();
41610         
41611         var o = this.options;
41612         var method = this.getMethod();
41613         var isPost = method == 'POST';
41614         if(o.clientValidation === false || this.form.isValid()){
41615             
41616             if (this.form.progressUrl) {
41617                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41618                     (new Date() * 1) + '' + Math.random());
41619                     
41620             } 
41621             
41622             
41623             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41624                 form:this.form.el.dom,
41625                 url:this.getUrl(!isPost),
41626                 method: method,
41627                 params:isPost ? this.getParams() : null,
41628                 isUpload: this.form.fileUpload
41629             }));
41630             
41631             this.uploadProgress();
41632
41633         }else if (o.clientValidation !== false){ // client validation failed
41634             this.failureType = Roo.form.Action.CLIENT_INVALID;
41635             this.form.afterAction(this, false);
41636         }
41637     },
41638
41639     success : function(response)
41640     {
41641         this.uploadComplete= true;
41642         if (this.haveProgress) {
41643             Roo.MessageBox.hide();
41644         }
41645         
41646         
41647         var result = this.processResponse(response);
41648         if(result === true || result.success){
41649             this.form.afterAction(this, true);
41650             return;
41651         }
41652         if(result.errors){
41653             this.form.markInvalid(result.errors);
41654             this.failureType = Roo.form.Action.SERVER_INVALID;
41655         }
41656         this.form.afterAction(this, false);
41657     },
41658     failure : function(response)
41659     {
41660         this.uploadComplete= true;
41661         if (this.haveProgress) {
41662             Roo.MessageBox.hide();
41663         }
41664         
41665         
41666         this.response = response;
41667         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41668         this.form.afterAction(this, false);
41669     },
41670     
41671     handleResponse : function(response){
41672         if(this.form.errorReader){
41673             var rs = this.form.errorReader.read(response);
41674             var errors = [];
41675             if(rs.records){
41676                 for(var i = 0, len = rs.records.length; i < len; i++) {
41677                     var r = rs.records[i];
41678                     errors[i] = r.data;
41679                 }
41680             }
41681             if(errors.length < 1){
41682                 errors = null;
41683             }
41684             return {
41685                 success : rs.success,
41686                 errors : errors
41687             };
41688         }
41689         var ret = false;
41690         try {
41691             ret = Roo.decode(response.responseText);
41692         } catch (e) {
41693             ret = {
41694                 success: false,
41695                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41696                 errors : []
41697             };
41698         }
41699         return ret;
41700         
41701     }
41702 });
41703
41704
41705 Roo.form.Action.Load = function(form, options){
41706     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41707     this.reader = this.form.reader;
41708 };
41709
41710 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41711     type : 'load',
41712
41713     run : function(){
41714         
41715         Roo.Ajax.request(Roo.apply(
41716                 this.createCallback(), {
41717                     method:this.getMethod(),
41718                     url:this.getUrl(false),
41719                     params:this.getParams()
41720         }));
41721     },
41722
41723     success : function(response){
41724         
41725         var result = this.processResponse(response);
41726         if(result === true || !result.success || !result.data){
41727             this.failureType = Roo.form.Action.LOAD_FAILURE;
41728             this.form.afterAction(this, false);
41729             return;
41730         }
41731         this.form.clearInvalid();
41732         this.form.setValues(result.data);
41733         this.form.afterAction(this, true);
41734     },
41735
41736     handleResponse : function(response){
41737         if(this.form.reader){
41738             var rs = this.form.reader.read(response);
41739             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41740             return {
41741                 success : rs.success,
41742                 data : data
41743             };
41744         }
41745         return Roo.decode(response.responseText);
41746     }
41747 });
41748
41749 Roo.form.Action.ACTION_TYPES = {
41750     'load' : Roo.form.Action.Load,
41751     'submit' : Roo.form.Action.Submit
41752 };/*
41753  * Based on:
41754  * Ext JS Library 1.1.1
41755  * Copyright(c) 2006-2007, Ext JS, LLC.
41756  *
41757  * Originally Released Under LGPL - original licence link has changed is not relivant.
41758  *
41759  * Fork - LGPL
41760  * <script type="text/javascript">
41761  */
41762  
41763 /**
41764  * @class Roo.form.Layout
41765  * @extends Roo.Component
41766  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41767  * @constructor
41768  * @param {Object} config Configuration options
41769  */
41770 Roo.form.Layout = function(config){
41771     var xitems = [];
41772     if (config.items) {
41773         xitems = config.items;
41774         delete config.items;
41775     }
41776     Roo.form.Layout.superclass.constructor.call(this, config);
41777     this.stack = [];
41778     Roo.each(xitems, this.addxtype, this);
41779      
41780 };
41781
41782 Roo.extend(Roo.form.Layout, Roo.Component, {
41783     /**
41784      * @cfg {String/Object} autoCreate
41785      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41786      */
41787     /**
41788      * @cfg {String/Object/Function} style
41789      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41790      * a function which returns such a specification.
41791      */
41792     /**
41793      * @cfg {String} labelAlign
41794      * Valid values are "left," "top" and "right" (defaults to "left")
41795      */
41796     /**
41797      * @cfg {Number} labelWidth
41798      * Fixed width in pixels of all field labels (defaults to undefined)
41799      */
41800     /**
41801      * @cfg {Boolean} clear
41802      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41803      */
41804     clear : true,
41805     /**
41806      * @cfg {String} labelSeparator
41807      * The separator to use after field labels (defaults to ':')
41808      */
41809     labelSeparator : ':',
41810     /**
41811      * @cfg {Boolean} hideLabels
41812      * True to suppress the display of field labels in this layout (defaults to false)
41813      */
41814     hideLabels : false,
41815
41816     // private
41817     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41818     
41819     isLayout : true,
41820     
41821     // private
41822     onRender : function(ct, position){
41823         if(this.el){ // from markup
41824             this.el = Roo.get(this.el);
41825         }else {  // generate
41826             var cfg = this.getAutoCreate();
41827             this.el = ct.createChild(cfg, position);
41828         }
41829         if(this.style){
41830             this.el.applyStyles(this.style);
41831         }
41832         if(this.labelAlign){
41833             this.el.addClass('x-form-label-'+this.labelAlign);
41834         }
41835         if(this.hideLabels){
41836             this.labelStyle = "display:none";
41837             this.elementStyle = "padding-left:0;";
41838         }else{
41839             if(typeof this.labelWidth == 'number'){
41840                 this.labelStyle = "width:"+this.labelWidth+"px;";
41841                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41842             }
41843             if(this.labelAlign == 'top'){
41844                 this.labelStyle = "width:auto;";
41845                 this.elementStyle = "padding-left:0;";
41846             }
41847         }
41848         var stack = this.stack;
41849         var slen = stack.length;
41850         if(slen > 0){
41851             if(!this.fieldTpl){
41852                 var t = new Roo.Template(
41853                     '<div class="x-form-item {5}">',
41854                         '<label for="{0}" style="{2}">{1}{4}</label>',
41855                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41856                         '</div>',
41857                     '</div><div class="x-form-clear-left"></div>'
41858                 );
41859                 t.disableFormats = true;
41860                 t.compile();
41861                 Roo.form.Layout.prototype.fieldTpl = t;
41862             }
41863             for(var i = 0; i < slen; i++) {
41864                 if(stack[i].isFormField){
41865                     this.renderField(stack[i]);
41866                 }else{
41867                     this.renderComponent(stack[i]);
41868                 }
41869             }
41870         }
41871         if(this.clear){
41872             this.el.createChild({cls:'x-form-clear'});
41873         }
41874     },
41875
41876     // private
41877     renderField : function(f){
41878         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41879                f.id, //0
41880                f.fieldLabel, //1
41881                f.labelStyle||this.labelStyle||'', //2
41882                this.elementStyle||'', //3
41883                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41884                f.itemCls||this.itemCls||''  //5
41885        ], true).getPrevSibling());
41886     },
41887
41888     // private
41889     renderComponent : function(c){
41890         c.render(c.isLayout ? this.el : this.el.createChild());    
41891     },
41892     /**
41893      * Adds a object form elements (using the xtype property as the factory method.)
41894      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41895      * @param {Object} config 
41896      */
41897     addxtype : function(o)
41898     {
41899         // create the lement.
41900         o.form = this.form;
41901         var fe = Roo.factory(o, Roo.form);
41902         this.form.allItems.push(fe);
41903         this.stack.push(fe);
41904         
41905         if (fe.isFormField) {
41906             this.form.items.add(fe);
41907         }
41908          
41909         return fe;
41910     }
41911 });
41912
41913 /**
41914  * @class Roo.form.Column
41915  * @extends Roo.form.Layout
41916  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41917  * @constructor
41918  * @param {Object} config Configuration options
41919  */
41920 Roo.form.Column = function(config){
41921     Roo.form.Column.superclass.constructor.call(this, config);
41922 };
41923
41924 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41925     /**
41926      * @cfg {Number/String} width
41927      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41928      */
41929     /**
41930      * @cfg {String/Object} autoCreate
41931      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41932      */
41933
41934     // private
41935     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41936
41937     // private
41938     onRender : function(ct, position){
41939         Roo.form.Column.superclass.onRender.call(this, ct, position);
41940         if(this.width){
41941             this.el.setWidth(this.width);
41942         }
41943     }
41944 });
41945
41946
41947 /**
41948  * @class Roo.form.Row
41949  * @extends Roo.form.Layout
41950  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41951  * @constructor
41952  * @param {Object} config Configuration options
41953  */
41954
41955  
41956 Roo.form.Row = function(config){
41957     Roo.form.Row.superclass.constructor.call(this, config);
41958 };
41959  
41960 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41961       /**
41962      * @cfg {Number/String} width
41963      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41964      */
41965     /**
41966      * @cfg {Number/String} height
41967      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41968      */
41969     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41970     
41971     padWidth : 20,
41972     // private
41973     onRender : function(ct, position){
41974         //console.log('row render');
41975         if(!this.rowTpl){
41976             var t = new Roo.Template(
41977                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41978                     '<label for="{0}" style="{2}">{1}{4}</label>',
41979                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41980                     '</div>',
41981                 '</div>'
41982             );
41983             t.disableFormats = true;
41984             t.compile();
41985             Roo.form.Layout.prototype.rowTpl = t;
41986         }
41987         this.fieldTpl = this.rowTpl;
41988         
41989         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41990         var labelWidth = 100;
41991         
41992         if ((this.labelAlign != 'top')) {
41993             if (typeof this.labelWidth == 'number') {
41994                 labelWidth = this.labelWidth
41995             }
41996             this.padWidth =  20 + labelWidth;
41997             
41998         }
41999         
42000         Roo.form.Column.superclass.onRender.call(this, ct, position);
42001         if(this.width){
42002             this.el.setWidth(this.width);
42003         }
42004         if(this.height){
42005             this.el.setHeight(this.height);
42006         }
42007     },
42008     
42009     // private
42010     renderField : function(f){
42011         f.fieldEl = this.fieldTpl.append(this.el, [
42012                f.id, f.fieldLabel,
42013                f.labelStyle||this.labelStyle||'',
42014                this.elementStyle||'',
42015                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42016                f.itemCls||this.itemCls||'',
42017                f.width ? f.width + this.padWidth : 160 + this.padWidth
42018        ],true);
42019     }
42020 });
42021  
42022
42023 /**
42024  * @class Roo.form.FieldSet
42025  * @extends Roo.form.Layout
42026  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42027  * @constructor
42028  * @param {Object} config Configuration options
42029  */
42030 Roo.form.FieldSet = function(config){
42031     Roo.form.FieldSet.superclass.constructor.call(this, config);
42032 };
42033
42034 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42035     /**
42036      * @cfg {String} legend
42037      * The text to display as the legend for the FieldSet (defaults to '')
42038      */
42039     /**
42040      * @cfg {String/Object} autoCreate
42041      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42042      */
42043
42044     // private
42045     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42046
42047     // private
42048     onRender : function(ct, position){
42049         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42050         if(this.legend){
42051             this.setLegend(this.legend);
42052         }
42053     },
42054
42055     // private
42056     setLegend : function(text){
42057         if(this.rendered){
42058             this.el.child('legend').update(text);
42059         }
42060     }
42061 });/*
42062  * Based on:
42063  * Ext JS Library 1.1.1
42064  * Copyright(c) 2006-2007, Ext JS, LLC.
42065  *
42066  * Originally Released Under LGPL - original licence link has changed is not relivant.
42067  *
42068  * Fork - LGPL
42069  * <script type="text/javascript">
42070  */
42071 /**
42072  * @class Roo.form.VTypes
42073  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42074  * @singleton
42075  */
42076 Roo.form.VTypes = function(){
42077     // closure these in so they are only created once.
42078     var alpha = /^[a-zA-Z_]+$/;
42079     var alphanum = /^[a-zA-Z0-9_]+$/;
42080     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42081     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42082
42083     // All these messages and functions are configurable
42084     return {
42085         /**
42086          * The function used to validate email addresses
42087          * @param {String} value The email address
42088          */
42089         'email' : function(v){
42090             return email.test(v);
42091         },
42092         /**
42093          * The error text to display when the email validation function returns false
42094          * @type String
42095          */
42096         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42097         /**
42098          * The keystroke filter mask to be applied on email input
42099          * @type RegExp
42100          */
42101         'emailMask' : /[a-z0-9_\.\-@]/i,
42102
42103         /**
42104          * The function used to validate URLs
42105          * @param {String} value The URL
42106          */
42107         'url' : function(v){
42108             return url.test(v);
42109         },
42110         /**
42111          * The error text to display when the url validation function returns false
42112          * @type String
42113          */
42114         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42115         
42116         /**
42117          * The function used to validate alpha values
42118          * @param {String} value The value
42119          */
42120         'alpha' : function(v){
42121             return alpha.test(v);
42122         },
42123         /**
42124          * The error text to display when the alpha validation function returns false
42125          * @type String
42126          */
42127         'alphaText' : 'This field should only contain letters and _',
42128         /**
42129          * The keystroke filter mask to be applied on alpha input
42130          * @type RegExp
42131          */
42132         'alphaMask' : /[a-z_]/i,
42133
42134         /**
42135          * The function used to validate alphanumeric values
42136          * @param {String} value The value
42137          */
42138         'alphanum' : function(v){
42139             return alphanum.test(v);
42140         },
42141         /**
42142          * The error text to display when the alphanumeric validation function returns false
42143          * @type String
42144          */
42145         'alphanumText' : 'This field should only contain letters, numbers and _',
42146         /**
42147          * The keystroke filter mask to be applied on alphanumeric input
42148          * @type RegExp
42149          */
42150         'alphanumMask' : /[a-z0-9_]/i
42151     };
42152 }();//<script type="text/javascript">
42153
42154 /**
42155  * @class Roo.form.FCKeditor
42156  * @extends Roo.form.TextArea
42157  * Wrapper around the FCKEditor http://www.fckeditor.net
42158  * @constructor
42159  * Creates a new FCKeditor
42160  * @param {Object} config Configuration options
42161  */
42162 Roo.form.FCKeditor = function(config){
42163     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42164     this.addEvents({
42165          /**
42166          * @event editorinit
42167          * Fired when the editor is initialized - you can add extra handlers here..
42168          * @param {FCKeditor} this
42169          * @param {Object} the FCK object.
42170          */
42171         editorinit : true
42172     });
42173     
42174     
42175 };
42176 Roo.form.FCKeditor.editors = { };
42177 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42178 {
42179     //defaultAutoCreate : {
42180     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42181     //},
42182     // private
42183     /**
42184      * @cfg {Object} fck options - see fck manual for details.
42185      */
42186     fckconfig : false,
42187     
42188     /**
42189      * @cfg {Object} fck toolbar set (Basic or Default)
42190      */
42191     toolbarSet : 'Basic',
42192     /**
42193      * @cfg {Object} fck BasePath
42194      */ 
42195     basePath : '/fckeditor/',
42196     
42197     
42198     frame : false,
42199     
42200     value : '',
42201     
42202    
42203     onRender : function(ct, position)
42204     {
42205         if(!this.el){
42206             this.defaultAutoCreate = {
42207                 tag: "textarea",
42208                 style:"width:300px;height:60px;",
42209                 autocomplete: "off"
42210             };
42211         }
42212         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42213         /*
42214         if(this.grow){
42215             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42216             if(this.preventScrollbars){
42217                 this.el.setStyle("overflow", "hidden");
42218             }
42219             this.el.setHeight(this.growMin);
42220         }
42221         */
42222         //console.log('onrender' + this.getId() );
42223         Roo.form.FCKeditor.editors[this.getId()] = this;
42224          
42225
42226         this.replaceTextarea() ;
42227         
42228     },
42229     
42230     getEditor : function() {
42231         return this.fckEditor;
42232     },
42233     /**
42234      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42235      * @param {Mixed} value The value to set
42236      */
42237     
42238     
42239     setValue : function(value)
42240     {
42241         //console.log('setValue: ' + value);
42242         
42243         if(typeof(value) == 'undefined') { // not sure why this is happending...
42244             return;
42245         }
42246         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42247         
42248         //if(!this.el || !this.getEditor()) {
42249         //    this.value = value;
42250             //this.setValue.defer(100,this,[value]);    
42251         //    return;
42252         //} 
42253         
42254         if(!this.getEditor()) {
42255             return;
42256         }
42257         
42258         this.getEditor().SetData(value);
42259         
42260         //
42261
42262     },
42263
42264     /**
42265      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42266      * @return {Mixed} value The field value
42267      */
42268     getValue : function()
42269     {
42270         
42271         if (this.frame && this.frame.dom.style.display == 'none') {
42272             return Roo.form.FCKeditor.superclass.getValue.call(this);
42273         }
42274         
42275         if(!this.el || !this.getEditor()) {
42276            
42277            // this.getValue.defer(100,this); 
42278             return this.value;
42279         }
42280        
42281         
42282         var value=this.getEditor().GetData();
42283         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42284         return Roo.form.FCKeditor.superclass.getValue.call(this);
42285         
42286
42287     },
42288
42289     /**
42290      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42291      * @return {Mixed} value The field value
42292      */
42293     getRawValue : function()
42294     {
42295         if (this.frame && this.frame.dom.style.display == 'none') {
42296             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42297         }
42298         
42299         if(!this.el || !this.getEditor()) {
42300             //this.getRawValue.defer(100,this); 
42301             return this.value;
42302             return;
42303         }
42304         
42305         
42306         
42307         var value=this.getEditor().GetData();
42308         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42309         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42310          
42311     },
42312     
42313     setSize : function(w,h) {
42314         
42315         
42316         
42317         //if (this.frame && this.frame.dom.style.display == 'none') {
42318         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42319         //    return;
42320         //}
42321         //if(!this.el || !this.getEditor()) {
42322         //    this.setSize.defer(100,this, [w,h]); 
42323         //    return;
42324         //}
42325         
42326         
42327         
42328         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42329         
42330         this.frame.dom.setAttribute('width', w);
42331         this.frame.dom.setAttribute('height', h);
42332         this.frame.setSize(w,h);
42333         
42334     },
42335     
42336     toggleSourceEdit : function(value) {
42337         
42338       
42339          
42340         this.el.dom.style.display = value ? '' : 'none';
42341         this.frame.dom.style.display = value ?  'none' : '';
42342         
42343     },
42344     
42345     
42346     focus: function(tag)
42347     {
42348         if (this.frame.dom.style.display == 'none') {
42349             return Roo.form.FCKeditor.superclass.focus.call(this);
42350         }
42351         if(!this.el || !this.getEditor()) {
42352             this.focus.defer(100,this, [tag]); 
42353             return;
42354         }
42355         
42356         
42357         
42358         
42359         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42360         this.getEditor().Focus();
42361         if (tgs.length) {
42362             if (!this.getEditor().Selection.GetSelection()) {
42363                 this.focus.defer(100,this, [tag]); 
42364                 return;
42365             }
42366             
42367             
42368             var r = this.getEditor().EditorDocument.createRange();
42369             r.setStart(tgs[0],0);
42370             r.setEnd(tgs[0],0);
42371             this.getEditor().Selection.GetSelection().removeAllRanges();
42372             this.getEditor().Selection.GetSelection().addRange(r);
42373             this.getEditor().Focus();
42374         }
42375         
42376     },
42377     
42378     
42379     
42380     replaceTextarea : function()
42381     {
42382         if ( document.getElementById( this.getId() + '___Frame' ) )
42383             return ;
42384         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42385         //{
42386             // We must check the elements firstly using the Id and then the name.
42387         var oTextarea = document.getElementById( this.getId() );
42388         
42389         var colElementsByName = document.getElementsByName( this.getId() ) ;
42390          
42391         oTextarea.style.display = 'none' ;
42392
42393         if ( oTextarea.tabIndex ) {            
42394             this.TabIndex = oTextarea.tabIndex ;
42395         }
42396         
42397         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42398         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42399         this.frame = Roo.get(this.getId() + '___Frame')
42400     },
42401     
42402     _getConfigHtml : function()
42403     {
42404         var sConfig = '' ;
42405
42406         for ( var o in this.fckconfig ) {
42407             sConfig += sConfig.length > 0  ? '&amp;' : '';
42408             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42409         }
42410
42411         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42412     },
42413     
42414     
42415     _getIFrameHtml : function()
42416     {
42417         var sFile = 'fckeditor.html' ;
42418         /* no idea what this is about..
42419         try
42420         {
42421             if ( (/fcksource=true/i).test( window.top.location.search ) )
42422                 sFile = 'fckeditor.original.html' ;
42423         }
42424         catch (e) { 
42425         */
42426
42427         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42428         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42429         
42430         
42431         var html = '<iframe id="' + this.getId() +
42432             '___Frame" src="' + sLink +
42433             '" width="' + this.width +
42434             '" height="' + this.height + '"' +
42435             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42436             ' frameborder="0" scrolling="no"></iframe>' ;
42437
42438         return html ;
42439     },
42440     
42441     _insertHtmlBefore : function( html, element )
42442     {
42443         if ( element.insertAdjacentHTML )       {
42444             // IE
42445             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42446         } else { // Gecko
42447             var oRange = document.createRange() ;
42448             oRange.setStartBefore( element ) ;
42449             var oFragment = oRange.createContextualFragment( html );
42450             element.parentNode.insertBefore( oFragment, element ) ;
42451         }
42452     }
42453     
42454     
42455   
42456     
42457     
42458     
42459     
42460
42461 });
42462
42463 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42464
42465 function FCKeditor_OnComplete(editorInstance){
42466     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42467     f.fckEditor = editorInstance;
42468     //console.log("loaded");
42469     f.fireEvent('editorinit', f, editorInstance);
42470
42471   
42472
42473  
42474
42475
42476
42477
42478
42479
42480
42481
42482
42483
42484
42485
42486
42487
42488
42489 //<script type="text/javascript">
42490 /**
42491  * @class Roo.form.GridField
42492  * @extends Roo.form.Field
42493  * Embed a grid (or editable grid into a form)
42494  * STATUS ALPHA
42495  * 
42496  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42497  * it needs 
42498  * xgrid.store = Roo.data.Store
42499  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42500  * xgrid.store.reader = Roo.data.JsonReader 
42501  * 
42502  * 
42503  * @constructor
42504  * Creates a new GridField
42505  * @param {Object} config Configuration options
42506  */
42507 Roo.form.GridField = function(config){
42508     Roo.form.GridField.superclass.constructor.call(this, config);
42509      
42510 };
42511
42512 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42513     /**
42514      * @cfg {Number} width  - used to restrict width of grid..
42515      */
42516     width : 100,
42517     /**
42518      * @cfg {Number} height - used to restrict height of grid..
42519      */
42520     height : 50,
42521      /**
42522      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42523          * 
42524          *}
42525      */
42526     xgrid : false, 
42527     /**
42528      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42529      * {tag: "input", type: "checkbox", autocomplete: "off"})
42530      */
42531    // defaultAutoCreate : { tag: 'div' },
42532     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42533     /**
42534      * @cfg {String} addTitle Text to include for adding a title.
42535      */
42536     addTitle : false,
42537     //
42538     onResize : function(){
42539         Roo.form.Field.superclass.onResize.apply(this, arguments);
42540     },
42541
42542     initEvents : function(){
42543         // Roo.form.Checkbox.superclass.initEvents.call(this);
42544         // has no events...
42545        
42546     },
42547
42548
42549     getResizeEl : function(){
42550         return this.wrap;
42551     },
42552
42553     getPositionEl : function(){
42554         return this.wrap;
42555     },
42556
42557     // private
42558     onRender : function(ct, position){
42559         
42560         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42561         var style = this.style;
42562         delete this.style;
42563         
42564         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42565         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42566         this.viewEl = this.wrap.createChild({ tag: 'div' });
42567         if (style) {
42568             this.viewEl.applyStyles(style);
42569         }
42570         if (this.width) {
42571             this.viewEl.setWidth(this.width);
42572         }
42573         if (this.height) {
42574             this.viewEl.setHeight(this.height);
42575         }
42576         //if(this.inputValue !== undefined){
42577         //this.setValue(this.value);
42578         
42579         
42580         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42581         
42582         
42583         this.grid.render();
42584         this.grid.getDataSource().on('remove', this.refreshValue, this);
42585         this.grid.getDataSource().on('update', this.refreshValue, this);
42586         this.grid.on('afteredit', this.refreshValue, this);
42587  
42588     },
42589      
42590     
42591     /**
42592      * Sets the value of the item. 
42593      * @param {String} either an object  or a string..
42594      */
42595     setValue : function(v){
42596         //this.value = v;
42597         v = v || []; // empty set..
42598         // this does not seem smart - it really only affects memoryproxy grids..
42599         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42600             var ds = this.grid.getDataSource();
42601             // assumes a json reader..
42602             var data = {}
42603             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42604             ds.loadData( data);
42605         }
42606         Roo.form.GridField.superclass.setValue.call(this, v);
42607         this.refreshValue();
42608         // should load data in the grid really....
42609     },
42610     
42611     // private
42612     refreshValue: function() {
42613          var val = [];
42614         this.grid.getDataSource().each(function(r) {
42615             val.push(r.data);
42616         });
42617         this.el.dom.value = Roo.encode(val);
42618     }
42619     
42620      
42621     
42622     
42623 });/*
42624  * Based on:
42625  * Ext JS Library 1.1.1
42626  * Copyright(c) 2006-2007, Ext JS, LLC.
42627  *
42628  * Originally Released Under LGPL - original licence link has changed is not relivant.
42629  *
42630  * Fork - LGPL
42631  * <script type="text/javascript">
42632  */
42633 /**
42634  * @class Roo.form.DisplayField
42635  * @extends Roo.form.Field
42636  * A generic Field to display non-editable data.
42637  * @constructor
42638  * Creates a new Display Field item.
42639  * @param {Object} config Configuration options
42640  */
42641 Roo.form.DisplayField = function(config){
42642     Roo.form.DisplayField.superclass.constructor.call(this, config);
42643     
42644 };
42645
42646 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42647     inputType:      'hidden',
42648     allowBlank:     true,
42649     readOnly:         true,
42650     
42651  
42652     /**
42653      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42654      */
42655     focusClass : undefined,
42656     /**
42657      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42658      */
42659     fieldClass: 'x-form-field',
42660     
42661      /**
42662      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42663      */
42664     valueRenderer: undefined,
42665     
42666     width: 100,
42667     /**
42668      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42669      * {tag: "input", type: "checkbox", autocomplete: "off"})
42670      */
42671      
42672  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42673
42674     onResize : function(){
42675         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42676         
42677     },
42678
42679     initEvents : function(){
42680         // Roo.form.Checkbox.superclass.initEvents.call(this);
42681         // has no events...
42682        
42683     },
42684
42685
42686     getResizeEl : function(){
42687         return this.wrap;
42688     },
42689
42690     getPositionEl : function(){
42691         return this.wrap;
42692     },
42693
42694     // private
42695     onRender : function(ct, position){
42696         
42697         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42698         //if(this.inputValue !== undefined){
42699         this.wrap = this.el.wrap();
42700         
42701         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42702         
42703         if (this.bodyStyle) {
42704             this.viewEl.applyStyles(this.bodyStyle);
42705         }
42706         //this.viewEl.setStyle('padding', '2px');
42707         
42708         this.setValue(this.value);
42709         
42710     },
42711 /*
42712     // private
42713     initValue : Roo.emptyFn,
42714
42715   */
42716
42717         // private
42718     onClick : function(){
42719         
42720     },
42721
42722     /**
42723      * Sets the checked state of the checkbox.
42724      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42725      */
42726     setValue : function(v){
42727         this.value = v;
42728         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42729         // this might be called before we have a dom element..
42730         if (!this.viewEl) {
42731             return;
42732         }
42733         this.viewEl.dom.innerHTML = html;
42734         Roo.form.DisplayField.superclass.setValue.call(this, v);
42735
42736     }
42737 });/*
42738  * 
42739  * Licence- LGPL
42740  * 
42741  */
42742
42743 /**
42744  * @class Roo.form.DayPicker
42745  * @extends Roo.form.Field
42746  * A Day picker show [M] [T] [W] ....
42747  * @constructor
42748  * Creates a new Day Picker
42749  * @param {Object} config Configuration options
42750  */
42751 Roo.form.DayPicker= function(config){
42752     Roo.form.DayPicker.superclass.constructor.call(this, config);
42753      
42754 };
42755
42756 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42757     /**
42758      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42759      */
42760     focusClass : undefined,
42761     /**
42762      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42763      */
42764     fieldClass: "x-form-field",
42765    
42766     /**
42767      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42768      * {tag: "input", type: "checkbox", autocomplete: "off"})
42769      */
42770     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42771     
42772    
42773     actionMode : 'viewEl', 
42774     //
42775     // private
42776  
42777     inputType : 'hidden',
42778     
42779      
42780     inputElement: false, // real input element?
42781     basedOn: false, // ????
42782     
42783     isFormField: true, // not sure where this is needed!!!!
42784
42785     onResize : function(){
42786         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42787         if(!this.boxLabel){
42788             this.el.alignTo(this.wrap, 'c-c');
42789         }
42790     },
42791
42792     initEvents : function(){
42793         Roo.form.Checkbox.superclass.initEvents.call(this);
42794         this.el.on("click", this.onClick,  this);
42795         this.el.on("change", this.onClick,  this);
42796     },
42797
42798
42799     getResizeEl : function(){
42800         return this.wrap;
42801     },
42802
42803     getPositionEl : function(){
42804         return this.wrap;
42805     },
42806
42807     
42808     // private
42809     onRender : function(ct, position){
42810         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42811        
42812         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42813         
42814         var r1 = '<table><tr>';
42815         var r2 = '<tr class="x-form-daypick-icons">';
42816         for (var i=0; i < 7; i++) {
42817             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42818             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42819         }
42820         
42821         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42822         viewEl.select('img').on('click', this.onClick, this);
42823         this.viewEl = viewEl;   
42824         
42825         
42826         // this will not work on Chrome!!!
42827         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42828         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42829         
42830         
42831           
42832
42833     },
42834
42835     // private
42836     initValue : Roo.emptyFn,
42837
42838     /**
42839      * Returns the checked state of the checkbox.
42840      * @return {Boolean} True if checked, else false
42841      */
42842     getValue : function(){
42843         return this.el.dom.value;
42844         
42845     },
42846
42847         // private
42848     onClick : function(e){ 
42849         //this.setChecked(!this.checked);
42850         Roo.get(e.target).toggleClass('x-menu-item-checked');
42851         this.refreshValue();
42852         //if(this.el.dom.checked != this.checked){
42853         //    this.setValue(this.el.dom.checked);
42854        // }
42855     },
42856     
42857     // private
42858     refreshValue : function()
42859     {
42860         var val = '';
42861         this.viewEl.select('img',true).each(function(e,i,n)  {
42862             val += e.is(".x-menu-item-checked") ? String(n) : '';
42863         });
42864         this.setValue(val, true);
42865     },
42866
42867     /**
42868      * Sets the checked state of the checkbox.
42869      * On is always based on a string comparison between inputValue and the param.
42870      * @param {Boolean/String} value - the value to set 
42871      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42872      */
42873     setValue : function(v,suppressEvent){
42874         if (!this.el.dom) {
42875             return;
42876         }
42877         var old = this.el.dom.value ;
42878         this.el.dom.value = v;
42879         if (suppressEvent) {
42880             return ;
42881         }
42882          
42883         // update display..
42884         this.viewEl.select('img',true).each(function(e,i,n)  {
42885             
42886             var on = e.is(".x-menu-item-checked");
42887             var newv = v.indexOf(String(n)) > -1;
42888             if (on != newv) {
42889                 e.toggleClass('x-menu-item-checked');
42890             }
42891             
42892         });
42893         
42894         
42895         this.fireEvent('change', this, v, old);
42896         
42897         
42898     },
42899    
42900     // handle setting of hidden value by some other method!!?!?
42901     setFromHidden: function()
42902     {
42903         if(!this.el){
42904             return;
42905         }
42906         //console.log("SET FROM HIDDEN");
42907         //alert('setFrom hidden');
42908         this.setValue(this.el.dom.value);
42909     },
42910     
42911     onDestroy : function()
42912     {
42913         if(this.viewEl){
42914             Roo.get(this.viewEl).remove();
42915         }
42916          
42917         Roo.form.DayPicker.superclass.onDestroy.call(this);
42918     }
42919
42920 });//<script type="text/javasscript">
42921  
42922
42923 /**
42924  * @class Roo.DDView
42925  * A DnD enabled version of Roo.View.
42926  * @param {Element/String} container The Element in which to create the View.
42927  * @param {String} tpl The template string used to create the markup for each element of the View
42928  * @param {Object} config The configuration properties. These include all the config options of
42929  * {@link Roo.View} plus some specific to this class.<br>
42930  * <p>
42931  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42932  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42933  * <p>
42934  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42935 .x-view-drag-insert-above {
42936         border-top:1px dotted #3366cc;
42937 }
42938 .x-view-drag-insert-below {
42939         border-bottom:1px dotted #3366cc;
42940 }
42941 </code></pre>
42942  * 
42943  */
42944  
42945 Roo.DDView = function(container, tpl, config) {
42946     Roo.DDView.superclass.constructor.apply(this, arguments);
42947     this.getEl().setStyle("outline", "0px none");
42948     this.getEl().unselectable();
42949     if (this.dragGroup) {
42950                 this.setDraggable(this.dragGroup.split(","));
42951     }
42952     if (this.dropGroup) {
42953                 this.setDroppable(this.dropGroup.split(","));
42954     }
42955     if (this.deletable) {
42956         this.setDeletable();
42957     }
42958     this.isDirtyFlag = false;
42959         this.addEvents({
42960                 "drop" : true
42961         });
42962 };
42963
42964 Roo.extend(Roo.DDView, Roo.View, {
42965 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42966 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42967 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42968 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42969
42970         isFormField: true,
42971
42972         reset: Roo.emptyFn,
42973         
42974         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42975
42976         validate: function() {
42977                 return true;
42978         },
42979         
42980         destroy: function() {
42981                 this.purgeListeners();
42982                 this.getEl.removeAllListeners();
42983                 this.getEl().remove();
42984                 if (this.dragZone) {
42985                         if (this.dragZone.destroy) {
42986                                 this.dragZone.destroy();
42987                         }
42988                 }
42989                 if (this.dropZone) {
42990                         if (this.dropZone.destroy) {
42991                                 this.dropZone.destroy();
42992                         }
42993                 }
42994         },
42995
42996 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42997         getName: function() {
42998                 return this.name;
42999         },
43000
43001 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43002         setValue: function(v) {
43003                 if (!this.store) {
43004                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43005                 }
43006                 var data = {};
43007                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43008                 this.store.proxy = new Roo.data.MemoryProxy(data);
43009                 this.store.load();
43010         },
43011
43012 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43013         getValue: function() {
43014                 var result = '(';
43015                 this.store.each(function(rec) {
43016                         result += rec.id + ',';
43017                 });
43018                 return result.substr(0, result.length - 1) + ')';
43019         },
43020         
43021         getIds: function() {
43022                 var i = 0, result = new Array(this.store.getCount());
43023                 this.store.each(function(rec) {
43024                         result[i++] = rec.id;
43025                 });
43026                 return result;
43027         },
43028         
43029         isDirty: function() {
43030                 return this.isDirtyFlag;
43031         },
43032
43033 /**
43034  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43035  *      whole Element becomes the target, and this causes the drop gesture to append.
43036  */
43037     getTargetFromEvent : function(e) {
43038                 var target = e.getTarget();
43039                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43040                 target = target.parentNode;
43041                 }
43042                 if (!target) {
43043                         target = this.el.dom.lastChild || this.el.dom;
43044                 }
43045                 return target;
43046     },
43047
43048 /**
43049  *      Create the drag data which consists of an object which has the property "ddel" as
43050  *      the drag proxy element. 
43051  */
43052     getDragData : function(e) {
43053         var target = this.findItemFromChild(e.getTarget());
43054                 if(target) {
43055                         this.handleSelection(e);
43056                         var selNodes = this.getSelectedNodes();
43057             var dragData = {
43058                 source: this,
43059                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43060                 nodes: selNodes,
43061                 records: []
43062                         };
43063                         var selectedIndices = this.getSelectedIndexes();
43064                         for (var i = 0; i < selectedIndices.length; i++) {
43065                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43066                         }
43067                         if (selNodes.length == 1) {
43068                                 dragData.ddel = target.cloneNode(true); // the div element
43069                         } else {
43070                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43071                                 div.className = 'multi-proxy';
43072                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43073                                         div.appendChild(selNodes[i].cloneNode(true));
43074                                 }
43075                                 dragData.ddel = div;
43076                         }
43077             //console.log(dragData)
43078             //console.log(dragData.ddel.innerHTML)
43079                         return dragData;
43080                 }
43081         //console.log('nodragData')
43082                 return false;
43083     },
43084     
43085 /**     Specify to which ddGroup items in this DDView may be dragged. */
43086     setDraggable: function(ddGroup) {
43087         if (ddGroup instanceof Array) {
43088                 Roo.each(ddGroup, this.setDraggable, this);
43089                 return;
43090         }
43091         if (this.dragZone) {
43092                 this.dragZone.addToGroup(ddGroup);
43093         } else {
43094                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43095                                 containerScroll: true,
43096                                 ddGroup: ddGroup 
43097
43098                         });
43099 //                      Draggability implies selection. DragZone's mousedown selects the element.
43100                         if (!this.multiSelect) { this.singleSelect = true; }
43101
43102 //                      Wire the DragZone's handlers up to methods in *this*
43103                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43104                 }
43105     },
43106
43107 /**     Specify from which ddGroup this DDView accepts drops. */
43108     setDroppable: function(ddGroup) {
43109         if (ddGroup instanceof Array) {
43110                 Roo.each(ddGroup, this.setDroppable, this);
43111                 return;
43112         }
43113         if (this.dropZone) {
43114                 this.dropZone.addToGroup(ddGroup);
43115         } else {
43116                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43117                                 containerScroll: true,
43118                                 ddGroup: ddGroup
43119                         });
43120
43121 //                      Wire the DropZone's handlers up to methods in *this*
43122                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43123                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43124                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43125                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43126                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43127                 }
43128     },
43129
43130 /**     Decide whether to drop above or below a View node. */
43131     getDropPoint : function(e, n, dd){
43132         if (n == this.el.dom) { return "above"; }
43133                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43134                 var c = t + (b - t) / 2;
43135                 var y = Roo.lib.Event.getPageY(e);
43136                 if(y <= c) {
43137                         return "above";
43138                 }else{
43139                         return "below";
43140                 }
43141     },
43142
43143     onNodeEnter : function(n, dd, e, data){
43144                 return false;
43145     },
43146     
43147     onNodeOver : function(n, dd, e, data){
43148                 var pt = this.getDropPoint(e, n, dd);
43149                 // set the insert point style on the target node
43150                 var dragElClass = this.dropNotAllowed;
43151                 if (pt) {
43152                         var targetElClass;
43153                         if (pt == "above"){
43154                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43155                                 targetElClass = "x-view-drag-insert-above";
43156                         } else {
43157                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43158                                 targetElClass = "x-view-drag-insert-below";
43159                         }
43160                         if (this.lastInsertClass != targetElClass){
43161                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43162                                 this.lastInsertClass = targetElClass;
43163                         }
43164                 }
43165                 return dragElClass;
43166         },
43167
43168     onNodeOut : function(n, dd, e, data){
43169                 this.removeDropIndicators(n);
43170     },
43171
43172     onNodeDrop : function(n, dd, e, data){
43173         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43174                 return false;
43175         }
43176         var pt = this.getDropPoint(e, n, dd);
43177                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43178                 if (pt == "below") { insertAt++; }
43179                 for (var i = 0; i < data.records.length; i++) {
43180                         var r = data.records[i];
43181                         var dup = this.store.getById(r.id);
43182                         if (dup && (dd != this.dragZone)) {
43183                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43184                         } else {
43185                                 if (data.copy) {
43186                                         this.store.insert(insertAt++, r.copy());
43187                                 } else {
43188                                         data.source.isDirtyFlag = true;
43189                                         r.store.remove(r);
43190                                         this.store.insert(insertAt++, r);
43191                                 }
43192                                 this.isDirtyFlag = true;
43193                         }
43194                 }
43195                 this.dragZone.cachedTarget = null;
43196                 return true;
43197     },
43198
43199     removeDropIndicators : function(n){
43200                 if(n){
43201                         Roo.fly(n).removeClass([
43202                                 "x-view-drag-insert-above",
43203                                 "x-view-drag-insert-below"]);
43204                         this.lastInsertClass = "_noclass";
43205                 }
43206     },
43207
43208 /**
43209  *      Utility method. Add a delete option to the DDView's context menu.
43210  *      @param {String} imageUrl The URL of the "delete" icon image.
43211  */
43212         setDeletable: function(imageUrl) {
43213                 if (!this.singleSelect && !this.multiSelect) {
43214                         this.singleSelect = true;
43215                 }
43216                 var c = this.getContextMenu();
43217                 this.contextMenu.on("itemclick", function(item) {
43218                         switch (item.id) {
43219                                 case "delete":
43220                                         this.remove(this.getSelectedIndexes());
43221                                         break;
43222                         }
43223                 }, this);
43224                 this.contextMenu.add({
43225                         icon: imageUrl,
43226                         id: "delete",
43227                         text: 'Delete'
43228                 });
43229         },
43230         
43231 /**     Return the context menu for this DDView. */
43232         getContextMenu: function() {
43233                 if (!this.contextMenu) {
43234 //                      Create the View's context menu
43235                         this.contextMenu = new Roo.menu.Menu({
43236                                 id: this.id + "-contextmenu"
43237                         });
43238                         this.el.on("contextmenu", this.showContextMenu, this);
43239                 }
43240                 return this.contextMenu;
43241         },
43242         
43243         disableContextMenu: function() {
43244                 if (this.contextMenu) {
43245                         this.el.un("contextmenu", this.showContextMenu, this);
43246                 }
43247         },
43248
43249         showContextMenu: function(e, item) {
43250         item = this.findItemFromChild(e.getTarget());
43251                 if (item) {
43252                         e.stopEvent();
43253                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43254                         this.contextMenu.showAt(e.getXY());
43255             }
43256     },
43257
43258 /**
43259  *      Remove {@link Roo.data.Record}s at the specified indices.
43260  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43261  */
43262     remove: function(selectedIndices) {
43263                 selectedIndices = [].concat(selectedIndices);
43264                 for (var i = 0; i < selectedIndices.length; i++) {
43265                         var rec = this.store.getAt(selectedIndices[i]);
43266                         this.store.remove(rec);
43267                 }
43268     },
43269
43270 /**
43271  *      Double click fires the event, but also, if this is draggable, and there is only one other
43272  *      related DropZone, it transfers the selected node.
43273  */
43274     onDblClick : function(e){
43275         var item = this.findItemFromChild(e.getTarget());
43276         if(item){
43277             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43278                 return false;
43279             }
43280             if (this.dragGroup) {
43281                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43282                     while (targets.indexOf(this.dropZone) > -1) {
43283                             targets.remove(this.dropZone);
43284                                 }
43285                     if (targets.length == 1) {
43286                                         this.dragZone.cachedTarget = null;
43287                         var el = Roo.get(targets[0].getEl());
43288                         var box = el.getBox(true);
43289                         targets[0].onNodeDrop(el.dom, {
43290                                 target: el.dom,
43291                                 xy: [box.x, box.y + box.height - 1]
43292                         }, null, this.getDragData(e));
43293                     }
43294                 }
43295         }
43296     },
43297     
43298     handleSelection: function(e) {
43299                 this.dragZone.cachedTarget = null;
43300         var item = this.findItemFromChild(e.getTarget());
43301         if (!item) {
43302                 this.clearSelections(true);
43303                 return;
43304         }
43305                 if (item && (this.multiSelect || this.singleSelect)){
43306                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43307                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43308                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43309                                 this.unselect(item);
43310                         } else {
43311                                 this.select(item, this.multiSelect && e.ctrlKey);
43312                                 this.lastSelection = item;
43313                         }
43314                 }
43315     },
43316
43317     onItemClick : function(item, index, e){
43318                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43319                         return false;
43320                 }
43321                 return true;
43322     },
43323
43324     unselect : function(nodeInfo, suppressEvent){
43325                 var node = this.getNode(nodeInfo);
43326                 if(node && this.isSelected(node)){
43327                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43328                                 Roo.fly(node).removeClass(this.selectedClass);
43329                                 this.selections.remove(node);
43330                                 if(!suppressEvent){
43331                                         this.fireEvent("selectionchange", this, this.selections);
43332                                 }
43333                         }
43334                 }
43335     }
43336 });
43337 /*
43338  * Based on:
43339  * Ext JS Library 1.1.1
43340  * Copyright(c) 2006-2007, Ext JS, LLC.
43341  *
43342  * Originally Released Under LGPL - original licence link has changed is not relivant.
43343  *
43344  * Fork - LGPL
43345  * <script type="text/javascript">
43346  */
43347  
43348 /**
43349  * @class Roo.LayoutManager
43350  * @extends Roo.util.Observable
43351  * Base class for layout managers.
43352  */
43353 Roo.LayoutManager = function(container, config){
43354     Roo.LayoutManager.superclass.constructor.call(this);
43355     this.el = Roo.get(container);
43356     // ie scrollbar fix
43357     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43358         document.body.scroll = "no";
43359     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43360         this.el.position('relative');
43361     }
43362     this.id = this.el.id;
43363     this.el.addClass("x-layout-container");
43364     /** false to disable window resize monitoring @type Boolean */
43365     this.monitorWindowResize = true;
43366     this.regions = {};
43367     this.addEvents({
43368         /**
43369          * @event layout
43370          * Fires when a layout is performed. 
43371          * @param {Roo.LayoutManager} this
43372          */
43373         "layout" : true,
43374         /**
43375          * @event regionresized
43376          * Fires when the user resizes a region. 
43377          * @param {Roo.LayoutRegion} region The resized region
43378          * @param {Number} newSize The new size (width for east/west, height for north/south)
43379          */
43380         "regionresized" : true,
43381         /**
43382          * @event regioncollapsed
43383          * Fires when a region is collapsed. 
43384          * @param {Roo.LayoutRegion} region The collapsed region
43385          */
43386         "regioncollapsed" : true,
43387         /**
43388          * @event regionexpanded
43389          * Fires when a region is expanded.  
43390          * @param {Roo.LayoutRegion} region The expanded region
43391          */
43392         "regionexpanded" : true
43393     });
43394     this.updating = false;
43395     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43396 };
43397
43398 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43399     /**
43400      * Returns true if this layout is currently being updated
43401      * @return {Boolean}
43402      */
43403     isUpdating : function(){
43404         return this.updating; 
43405     },
43406     
43407     /**
43408      * Suspend the LayoutManager from doing auto-layouts while
43409      * making multiple add or remove calls
43410      */
43411     beginUpdate : function(){
43412         this.updating = true;    
43413     },
43414     
43415     /**
43416      * Restore auto-layouts and optionally disable the manager from performing a layout
43417      * @param {Boolean} noLayout true to disable a layout update 
43418      */
43419     endUpdate : function(noLayout){
43420         this.updating = false;
43421         if(!noLayout){
43422             this.layout();
43423         }    
43424     },
43425     
43426     layout: function(){
43427         
43428     },
43429     
43430     onRegionResized : function(region, newSize){
43431         this.fireEvent("regionresized", region, newSize);
43432         this.layout();
43433     },
43434     
43435     onRegionCollapsed : function(region){
43436         this.fireEvent("regioncollapsed", region);
43437     },
43438     
43439     onRegionExpanded : function(region){
43440         this.fireEvent("regionexpanded", region);
43441     },
43442         
43443     /**
43444      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43445      * performs box-model adjustments.
43446      * @return {Object} The size as an object {width: (the width), height: (the height)}
43447      */
43448     getViewSize : function(){
43449         var size;
43450         if(this.el.dom != document.body){
43451             size = this.el.getSize();
43452         }else{
43453             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43454         }
43455         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43456         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43457         return size;
43458     },
43459     
43460     /**
43461      * Returns the Element this layout is bound to.
43462      * @return {Roo.Element}
43463      */
43464     getEl : function(){
43465         return this.el;
43466     },
43467     
43468     /**
43469      * Returns the specified region.
43470      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43471      * @return {Roo.LayoutRegion}
43472      */
43473     getRegion : function(target){
43474         return this.regions[target.toLowerCase()];
43475     },
43476     
43477     onWindowResize : function(){
43478         if(this.monitorWindowResize){
43479             this.layout();
43480         }
43481     }
43482 });/*
43483  * Based on:
43484  * Ext JS Library 1.1.1
43485  * Copyright(c) 2006-2007, Ext JS, LLC.
43486  *
43487  * Originally Released Under LGPL - original licence link has changed is not relivant.
43488  *
43489  * Fork - LGPL
43490  * <script type="text/javascript">
43491  */
43492 /**
43493  * @class Roo.BorderLayout
43494  * @extends Roo.LayoutManager
43495  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43496  * please see: <br><br>
43497  * <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>
43498  * <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>
43499  * Example:
43500  <pre><code>
43501  var layout = new Roo.BorderLayout(document.body, {
43502     north: {
43503         initialSize: 25,
43504         titlebar: false
43505     },
43506     west: {
43507         split:true,
43508         initialSize: 200,
43509         minSize: 175,
43510         maxSize: 400,
43511         titlebar: true,
43512         collapsible: true
43513     },
43514     east: {
43515         split:true,
43516         initialSize: 202,
43517         minSize: 175,
43518         maxSize: 400,
43519         titlebar: true,
43520         collapsible: true
43521     },
43522     south: {
43523         split:true,
43524         initialSize: 100,
43525         minSize: 100,
43526         maxSize: 200,
43527         titlebar: true,
43528         collapsible: true
43529     },
43530     center: {
43531         titlebar: true,
43532         autoScroll:true,
43533         resizeTabs: true,
43534         minTabWidth: 50,
43535         preferredTabWidth: 150
43536     }
43537 });
43538
43539 // shorthand
43540 var CP = Roo.ContentPanel;
43541
43542 layout.beginUpdate();
43543 layout.add("north", new CP("north", "North"));
43544 layout.add("south", new CP("south", {title: "South", closable: true}));
43545 layout.add("west", new CP("west", {title: "West"}));
43546 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43547 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43548 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43549 layout.getRegion("center").showPanel("center1");
43550 layout.endUpdate();
43551 </code></pre>
43552
43553 <b>The container the layout is rendered into can be either the body element or any other element.
43554 If it is not the body element, the container needs to either be an absolute positioned element,
43555 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43556 the container size if it is not the body element.</b>
43557
43558 * @constructor
43559 * Create a new BorderLayout
43560 * @param {String/HTMLElement/Element} container The container this layout is bound to
43561 * @param {Object} config Configuration options
43562  */
43563 Roo.BorderLayout = function(container, config){
43564     config = config || {};
43565     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43566     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43567     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43568         var target = this.factory.validRegions[i];
43569         if(config[target]){
43570             this.addRegion(target, config[target]);
43571         }
43572     }
43573 };
43574
43575 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43576     /**
43577      * Creates and adds a new region if it doesn't already exist.
43578      * @param {String} target The target region key (north, south, east, west or center).
43579      * @param {Object} config The regions config object
43580      * @return {BorderLayoutRegion} The new region
43581      */
43582     addRegion : function(target, config){
43583         if(!this.regions[target]){
43584             var r = this.factory.create(target, this, config);
43585             this.bindRegion(target, r);
43586         }
43587         return this.regions[target];
43588     },
43589
43590     // private (kinda)
43591     bindRegion : function(name, r){
43592         this.regions[name] = r;
43593         r.on("visibilitychange", this.layout, this);
43594         r.on("paneladded", this.layout, this);
43595         r.on("panelremoved", this.layout, this);
43596         r.on("invalidated", this.layout, this);
43597         r.on("resized", this.onRegionResized, this);
43598         r.on("collapsed", this.onRegionCollapsed, this);
43599         r.on("expanded", this.onRegionExpanded, this);
43600     },
43601
43602     /**
43603      * Performs a layout update.
43604      */
43605     layout : function(){
43606         if(this.updating) return;
43607         var size = this.getViewSize();
43608         var w = size.width;
43609         var h = size.height;
43610         var centerW = w;
43611         var centerH = h;
43612         var centerY = 0;
43613         var centerX = 0;
43614         //var x = 0, y = 0;
43615
43616         var rs = this.regions;
43617         var north = rs["north"];
43618         var south = rs["south"]; 
43619         var west = rs["west"];
43620         var east = rs["east"];
43621         var center = rs["center"];
43622         //if(this.hideOnLayout){ // not supported anymore
43623             //c.el.setStyle("display", "none");
43624         //}
43625         if(north && north.isVisible()){
43626             var b = north.getBox();
43627             var m = north.getMargins();
43628             b.width = w - (m.left+m.right);
43629             b.x = m.left;
43630             b.y = m.top;
43631             centerY = b.height + b.y + m.bottom;
43632             centerH -= centerY;
43633             north.updateBox(this.safeBox(b));
43634         }
43635         if(south && south.isVisible()){
43636             var b = south.getBox();
43637             var m = south.getMargins();
43638             b.width = w - (m.left+m.right);
43639             b.x = m.left;
43640             var totalHeight = (b.height + m.top + m.bottom);
43641             b.y = h - totalHeight + m.top;
43642             centerH -= totalHeight;
43643             south.updateBox(this.safeBox(b));
43644         }
43645         if(west && west.isVisible()){
43646             var b = west.getBox();
43647             var m = west.getMargins();
43648             b.height = centerH - (m.top+m.bottom);
43649             b.x = m.left;
43650             b.y = centerY + m.top;
43651             var totalWidth = (b.width + m.left + m.right);
43652             centerX += totalWidth;
43653             centerW -= totalWidth;
43654             west.updateBox(this.safeBox(b));
43655         }
43656         if(east && east.isVisible()){
43657             var b = east.getBox();
43658             var m = east.getMargins();
43659             b.height = centerH - (m.top+m.bottom);
43660             var totalWidth = (b.width + m.left + m.right);
43661             b.x = w - totalWidth + m.left;
43662             b.y = centerY + m.top;
43663             centerW -= totalWidth;
43664             east.updateBox(this.safeBox(b));
43665         }
43666         if(center){
43667             var m = center.getMargins();
43668             var centerBox = {
43669                 x: centerX + m.left,
43670                 y: centerY + m.top,
43671                 width: centerW - (m.left+m.right),
43672                 height: centerH - (m.top+m.bottom)
43673             };
43674             //if(this.hideOnLayout){
43675                 //center.el.setStyle("display", "block");
43676             //}
43677             center.updateBox(this.safeBox(centerBox));
43678         }
43679         this.el.repaint();
43680         this.fireEvent("layout", this);
43681     },
43682
43683     // private
43684     safeBox : function(box){
43685         box.width = Math.max(0, box.width);
43686         box.height = Math.max(0, box.height);
43687         return box;
43688     },
43689
43690     /**
43691      * Adds a ContentPanel (or subclass) to this layout.
43692      * @param {String} target The target region key (north, south, east, west or center).
43693      * @param {Roo.ContentPanel} panel The panel to add
43694      * @return {Roo.ContentPanel} The added panel
43695      */
43696     add : function(target, panel){
43697          
43698         target = target.toLowerCase();
43699         return this.regions[target].add(panel);
43700     },
43701
43702     /**
43703      * Remove a ContentPanel (or subclass) to this layout.
43704      * @param {String} target The target region key (north, south, east, west or center).
43705      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43706      * @return {Roo.ContentPanel} The removed panel
43707      */
43708     remove : function(target, panel){
43709         target = target.toLowerCase();
43710         return this.regions[target].remove(panel);
43711     },
43712
43713     /**
43714      * Searches all regions for a panel with the specified id
43715      * @param {String} panelId
43716      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43717      */
43718     findPanel : function(panelId){
43719         var rs = this.regions;
43720         for(var target in rs){
43721             if(typeof rs[target] != "function"){
43722                 var p = rs[target].getPanel(panelId);
43723                 if(p){
43724                     return p;
43725                 }
43726             }
43727         }
43728         return null;
43729     },
43730
43731     /**
43732      * Searches all regions for a panel with the specified id and activates (shows) it.
43733      * @param {String/ContentPanel} panelId The panels id or the panel itself
43734      * @return {Roo.ContentPanel} The shown panel or null
43735      */
43736     showPanel : function(panelId) {
43737       var rs = this.regions;
43738       for(var target in rs){
43739          var r = rs[target];
43740          if(typeof r != "function"){
43741             if(r.hasPanel(panelId)){
43742                return r.showPanel(panelId);
43743             }
43744          }
43745       }
43746       return null;
43747    },
43748
43749    /**
43750      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43751      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43752      */
43753     restoreState : function(provider){
43754         if(!provider){
43755             provider = Roo.state.Manager;
43756         }
43757         var sm = new Roo.LayoutStateManager();
43758         sm.init(this, provider);
43759     },
43760
43761     /**
43762      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43763      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43764      * a valid ContentPanel config object.  Example:
43765      * <pre><code>
43766 // Create the main layout
43767 var layout = new Roo.BorderLayout('main-ct', {
43768     west: {
43769         split:true,
43770         minSize: 175,
43771         titlebar: true
43772     },
43773     center: {
43774         title:'Components'
43775     }
43776 }, 'main-ct');
43777
43778 // Create and add multiple ContentPanels at once via configs
43779 layout.batchAdd({
43780    west: {
43781        id: 'source-files',
43782        autoCreate:true,
43783        title:'Ext Source Files',
43784        autoScroll:true,
43785        fitToFrame:true
43786    },
43787    center : {
43788        el: cview,
43789        autoScroll:true,
43790        fitToFrame:true,
43791        toolbar: tb,
43792        resizeEl:'cbody'
43793    }
43794 });
43795 </code></pre>
43796      * @param {Object} regions An object containing ContentPanel configs by region name
43797      */
43798     batchAdd : function(regions){
43799         this.beginUpdate();
43800         for(var rname in regions){
43801             var lr = this.regions[rname];
43802             if(lr){
43803                 this.addTypedPanels(lr, regions[rname]);
43804             }
43805         }
43806         this.endUpdate();
43807     },
43808
43809     // private
43810     addTypedPanels : function(lr, ps){
43811         if(typeof ps == 'string'){
43812             lr.add(new Roo.ContentPanel(ps));
43813         }
43814         else if(ps instanceof Array){
43815             for(var i =0, len = ps.length; i < len; i++){
43816                 this.addTypedPanels(lr, ps[i]);
43817             }
43818         }
43819         else if(!ps.events){ // raw config?
43820             var el = ps.el;
43821             delete ps.el; // prevent conflict
43822             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43823         }
43824         else {  // panel object assumed!
43825             lr.add(ps);
43826         }
43827     },
43828     /**
43829      * Adds a xtype elements to the layout.
43830      * <pre><code>
43831
43832 layout.addxtype({
43833        xtype : 'ContentPanel',
43834        region: 'west',
43835        items: [ .... ]
43836    }
43837 );
43838
43839 layout.addxtype({
43840         xtype : 'NestedLayoutPanel',
43841         region: 'west',
43842         layout: {
43843            center: { },
43844            west: { }   
43845         },
43846         items : [ ... list of content panels or nested layout panels.. ]
43847    }
43848 );
43849 </code></pre>
43850      * @param {Object} cfg Xtype definition of item to add.
43851      */
43852     addxtype : function(cfg)
43853     {
43854         // basically accepts a pannel...
43855         // can accept a layout region..!?!?
43856        // console.log('BorderLayout add ' + cfg.xtype)
43857         
43858         if (!cfg.xtype.match(/Panel$/)) {
43859             return false;
43860         }
43861         var ret = false;
43862         var region = cfg.region;
43863         delete cfg.region;
43864         
43865           
43866         var xitems = [];
43867         if (cfg.items) {
43868             xitems = cfg.items;
43869             delete cfg.items;
43870         }
43871         
43872         
43873         switch(cfg.xtype) 
43874         {
43875             case 'ContentPanel':  // ContentPanel (el, cfg)
43876             case 'ScrollPanel':  // ContentPanel (el, cfg)
43877                 if(cfg.autoCreate) {
43878                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43879                 } else {
43880                     var el = this.el.createChild();
43881                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43882                 }
43883                 
43884                 this.add(region, ret);
43885                 break;
43886             
43887             
43888             case 'TreePanel': // our new panel!
43889                 cfg.el = this.el.createChild();
43890                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43891                 this.add(region, ret);
43892                 break;
43893             
43894             case 'NestedLayoutPanel': 
43895                 // create a new Layout (which is  a Border Layout...
43896                 var el = this.el.createChild();
43897                 var clayout = cfg.layout;
43898                 delete cfg.layout;
43899                 clayout.items   = clayout.items  || [];
43900                 // replace this exitems with the clayout ones..
43901                 xitems = clayout.items;
43902                  
43903                 
43904                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43905                     cfg.background = false;
43906                 }
43907                 var layout = new Roo.BorderLayout(el, clayout);
43908                 
43909                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43910                 //console.log('adding nested layout panel '  + cfg.toSource());
43911                 this.add(region, ret);
43912                 
43913                 break;
43914                 
43915             case 'GridPanel': 
43916             
43917                 // needs grid and region
43918                 
43919                 //var el = this.getRegion(region).el.createChild();
43920                 var el = this.el.createChild();
43921                 // create the grid first...
43922                 
43923                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43924                 delete cfg.grid;
43925                 if (region == 'center' && this.active ) {
43926                     cfg.background = false;
43927                 }
43928                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43929                 
43930                 this.add(region, ret);
43931                 if (cfg.background) {
43932                     ret.on('activate', function(gp) {
43933                         if (!gp.grid.rendered) {
43934                             gp.grid.render();
43935                         }
43936                     });
43937                 } else {
43938                     grid.render();
43939                 }
43940                 break;
43941            
43942                
43943                 
43944                 
43945             default: 
43946                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43947                 return;
43948              // GridPanel (grid, cfg)
43949             
43950         }
43951         this.beginUpdate();
43952         // add children..
43953         Roo.each(xitems, function(i)  {
43954             ret.addxtype(i);
43955         });
43956         this.endUpdate();
43957         return ret;
43958         
43959     }
43960 });
43961
43962 /**
43963  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43964  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43965  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43966  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43967  * <pre><code>
43968 // shorthand
43969 var CP = Roo.ContentPanel;
43970
43971 var layout = Roo.BorderLayout.create({
43972     north: {
43973         initialSize: 25,
43974         titlebar: false,
43975         panels: [new CP("north", "North")]
43976     },
43977     west: {
43978         split:true,
43979         initialSize: 200,
43980         minSize: 175,
43981         maxSize: 400,
43982         titlebar: true,
43983         collapsible: true,
43984         panels: [new CP("west", {title: "West"})]
43985     },
43986     east: {
43987         split:true,
43988         initialSize: 202,
43989         minSize: 175,
43990         maxSize: 400,
43991         titlebar: true,
43992         collapsible: true,
43993         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43994     },
43995     south: {
43996         split:true,
43997         initialSize: 100,
43998         minSize: 100,
43999         maxSize: 200,
44000         titlebar: true,
44001         collapsible: true,
44002         panels: [new CP("south", {title: "South", closable: true})]
44003     },
44004     center: {
44005         titlebar: true,
44006         autoScroll:true,
44007         resizeTabs: true,
44008         minTabWidth: 50,
44009         preferredTabWidth: 150,
44010         panels: [
44011             new CP("center1", {title: "Close Me", closable: true}),
44012             new CP("center2", {title: "Center Panel", closable: false})
44013         ]
44014     }
44015 }, document.body);
44016
44017 layout.getRegion("center").showPanel("center1");
44018 </code></pre>
44019  * @param config
44020  * @param targetEl
44021  */
44022 Roo.BorderLayout.create = function(config, targetEl){
44023     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44024     layout.beginUpdate();
44025     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44026     for(var j = 0, jlen = regions.length; j < jlen; j++){
44027         var lr = regions[j];
44028         if(layout.regions[lr] && config[lr].panels){
44029             var r = layout.regions[lr];
44030             var ps = config[lr].panels;
44031             layout.addTypedPanels(r, ps);
44032         }
44033     }
44034     layout.endUpdate();
44035     return layout;
44036 };
44037
44038 // private
44039 Roo.BorderLayout.RegionFactory = {
44040     // private
44041     validRegions : ["north","south","east","west","center"],
44042
44043     // private
44044     create : function(target, mgr, config){
44045         target = target.toLowerCase();
44046         if(config.lightweight || config.basic){
44047             return new Roo.BasicLayoutRegion(mgr, config, target);
44048         }
44049         switch(target){
44050             case "north":
44051                 return new Roo.NorthLayoutRegion(mgr, config);
44052             case "south":
44053                 return new Roo.SouthLayoutRegion(mgr, config);
44054             case "east":
44055                 return new Roo.EastLayoutRegion(mgr, config);
44056             case "west":
44057                 return new Roo.WestLayoutRegion(mgr, config);
44058             case "center":
44059                 return new Roo.CenterLayoutRegion(mgr, config);
44060         }
44061         throw 'Layout region "'+target+'" not supported.';
44062     }
44063 };/*
44064  * Based on:
44065  * Ext JS Library 1.1.1
44066  * Copyright(c) 2006-2007, Ext JS, LLC.
44067  *
44068  * Originally Released Under LGPL - original licence link has changed is not relivant.
44069  *
44070  * Fork - LGPL
44071  * <script type="text/javascript">
44072  */
44073  
44074 /**
44075  * @class Roo.BasicLayoutRegion
44076  * @extends Roo.util.Observable
44077  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44078  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44079  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44080  */
44081 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44082     this.mgr = mgr;
44083     this.position  = pos;
44084     this.events = {
44085         /**
44086          * @scope Roo.BasicLayoutRegion
44087          */
44088         
44089         /**
44090          * @event beforeremove
44091          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44092          * @param {Roo.LayoutRegion} this
44093          * @param {Roo.ContentPanel} panel The panel
44094          * @param {Object} e The cancel event object
44095          */
44096         "beforeremove" : true,
44097         /**
44098          * @event invalidated
44099          * Fires when the layout for this region is changed.
44100          * @param {Roo.LayoutRegion} this
44101          */
44102         "invalidated" : true,
44103         /**
44104          * @event visibilitychange
44105          * Fires when this region is shown or hidden 
44106          * @param {Roo.LayoutRegion} this
44107          * @param {Boolean} visibility true or false
44108          */
44109         "visibilitychange" : true,
44110         /**
44111          * @event paneladded
44112          * Fires when a panel is added. 
44113          * @param {Roo.LayoutRegion} this
44114          * @param {Roo.ContentPanel} panel The panel
44115          */
44116         "paneladded" : true,
44117         /**
44118          * @event panelremoved
44119          * Fires when a panel is removed. 
44120          * @param {Roo.LayoutRegion} this
44121          * @param {Roo.ContentPanel} panel The panel
44122          */
44123         "panelremoved" : true,
44124         /**
44125          * @event collapsed
44126          * Fires when this region is collapsed.
44127          * @param {Roo.LayoutRegion} this
44128          */
44129         "collapsed" : true,
44130         /**
44131          * @event expanded
44132          * Fires when this region is expanded.
44133          * @param {Roo.LayoutRegion} this
44134          */
44135         "expanded" : true,
44136         /**
44137          * @event slideshow
44138          * Fires when this region is slid into view.
44139          * @param {Roo.LayoutRegion} this
44140          */
44141         "slideshow" : true,
44142         /**
44143          * @event slidehide
44144          * Fires when this region slides out of view. 
44145          * @param {Roo.LayoutRegion} this
44146          */
44147         "slidehide" : true,
44148         /**
44149          * @event panelactivated
44150          * Fires when a panel is activated. 
44151          * @param {Roo.LayoutRegion} this
44152          * @param {Roo.ContentPanel} panel The activated panel
44153          */
44154         "panelactivated" : true,
44155         /**
44156          * @event resized
44157          * Fires when the user resizes this region. 
44158          * @param {Roo.LayoutRegion} this
44159          * @param {Number} newSize The new size (width for east/west, height for north/south)
44160          */
44161         "resized" : true
44162     };
44163     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44164     this.panels = new Roo.util.MixedCollection();
44165     this.panels.getKey = this.getPanelId.createDelegate(this);
44166     this.box = null;
44167     this.activePanel = null;
44168     // ensure listeners are added...
44169     
44170     if (config.listeners || config.events) {
44171         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44172             listeners : config.listeners || {},
44173             events : config.events || {}
44174         });
44175     }
44176     
44177     if(skipConfig !== true){
44178         this.applyConfig(config);
44179     }
44180 };
44181
44182 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44183     getPanelId : function(p){
44184         return p.getId();
44185     },
44186     
44187     applyConfig : function(config){
44188         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44189         this.config = config;
44190         
44191     },
44192     
44193     /**
44194      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44195      * the width, for horizontal (north, south) the height.
44196      * @param {Number} newSize The new width or height
44197      */
44198     resizeTo : function(newSize){
44199         var el = this.el ? this.el :
44200                  (this.activePanel ? this.activePanel.getEl() : null);
44201         if(el){
44202             switch(this.position){
44203                 case "east":
44204                 case "west":
44205                     el.setWidth(newSize);
44206                     this.fireEvent("resized", this, newSize);
44207                 break;
44208                 case "north":
44209                 case "south":
44210                     el.setHeight(newSize);
44211                     this.fireEvent("resized", this, newSize);
44212                 break;                
44213             }
44214         }
44215     },
44216     
44217     getBox : function(){
44218         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44219     },
44220     
44221     getMargins : function(){
44222         return this.margins;
44223     },
44224     
44225     updateBox : function(box){
44226         this.box = box;
44227         var el = this.activePanel.getEl();
44228         el.dom.style.left = box.x + "px";
44229         el.dom.style.top = box.y + "px";
44230         this.activePanel.setSize(box.width, box.height);
44231     },
44232     
44233     /**
44234      * Returns the container element for this region.
44235      * @return {Roo.Element}
44236      */
44237     getEl : function(){
44238         return this.activePanel;
44239     },
44240     
44241     /**
44242      * Returns true if this region is currently visible.
44243      * @return {Boolean}
44244      */
44245     isVisible : function(){
44246         return this.activePanel ? true : false;
44247     },
44248     
44249     setActivePanel : function(panel){
44250         panel = this.getPanel(panel);
44251         if(this.activePanel && this.activePanel != panel){
44252             this.activePanel.setActiveState(false);
44253             this.activePanel.getEl().setLeftTop(-10000,-10000);
44254         }
44255         this.activePanel = panel;
44256         panel.setActiveState(true);
44257         if(this.box){
44258             panel.setSize(this.box.width, this.box.height);
44259         }
44260         this.fireEvent("panelactivated", this, panel);
44261         this.fireEvent("invalidated");
44262     },
44263     
44264     /**
44265      * Show the specified panel.
44266      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44267      * @return {Roo.ContentPanel} The shown panel or null
44268      */
44269     showPanel : function(panel){
44270         if(panel = this.getPanel(panel)){
44271             this.setActivePanel(panel);
44272         }
44273         return panel;
44274     },
44275     
44276     /**
44277      * Get the active panel for this region.
44278      * @return {Roo.ContentPanel} The active panel or null
44279      */
44280     getActivePanel : function(){
44281         return this.activePanel;
44282     },
44283     
44284     /**
44285      * Add the passed ContentPanel(s)
44286      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44287      * @return {Roo.ContentPanel} The panel added (if only one was added)
44288      */
44289     add : function(panel){
44290         if(arguments.length > 1){
44291             for(var i = 0, len = arguments.length; i < len; i++) {
44292                 this.add(arguments[i]);
44293             }
44294             return null;
44295         }
44296         if(this.hasPanel(panel)){
44297             this.showPanel(panel);
44298             return panel;
44299         }
44300         var el = panel.getEl();
44301         if(el.dom.parentNode != this.mgr.el.dom){
44302             this.mgr.el.dom.appendChild(el.dom);
44303         }
44304         if(panel.setRegion){
44305             panel.setRegion(this);
44306         }
44307         this.panels.add(panel);
44308         el.setStyle("position", "absolute");
44309         if(!panel.background){
44310             this.setActivePanel(panel);
44311             if(this.config.initialSize && this.panels.getCount()==1){
44312                 this.resizeTo(this.config.initialSize);
44313             }
44314         }
44315         this.fireEvent("paneladded", this, panel);
44316         return panel;
44317     },
44318     
44319     /**
44320      * Returns true if the panel is in this region.
44321      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44322      * @return {Boolean}
44323      */
44324     hasPanel : function(panel){
44325         if(typeof panel == "object"){ // must be panel obj
44326             panel = panel.getId();
44327         }
44328         return this.getPanel(panel) ? true : false;
44329     },
44330     
44331     /**
44332      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44333      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44334      * @param {Boolean} preservePanel Overrides the config preservePanel option
44335      * @return {Roo.ContentPanel} The panel that was removed
44336      */
44337     remove : function(panel, preservePanel){
44338         panel = this.getPanel(panel);
44339         if(!panel){
44340             return null;
44341         }
44342         var e = {};
44343         this.fireEvent("beforeremove", this, panel, e);
44344         if(e.cancel === true){
44345             return null;
44346         }
44347         var panelId = panel.getId();
44348         this.panels.removeKey(panelId);
44349         return panel;
44350     },
44351     
44352     /**
44353      * Returns the panel specified or null if it's not in this region.
44354      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44355      * @return {Roo.ContentPanel}
44356      */
44357     getPanel : function(id){
44358         if(typeof id == "object"){ // must be panel obj
44359             return id;
44360         }
44361         return this.panels.get(id);
44362     },
44363     
44364     /**
44365      * Returns this regions position (north/south/east/west/center).
44366      * @return {String} 
44367      */
44368     getPosition: function(){
44369         return this.position;    
44370     }
44371 });/*
44372  * Based on:
44373  * Ext JS Library 1.1.1
44374  * Copyright(c) 2006-2007, Ext JS, LLC.
44375  *
44376  * Originally Released Under LGPL - original licence link has changed is not relivant.
44377  *
44378  * Fork - LGPL
44379  * <script type="text/javascript">
44380  */
44381  
44382 /**
44383  * @class Roo.LayoutRegion
44384  * @extends Roo.BasicLayoutRegion
44385  * This class represents a region in a layout manager.
44386  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44387  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44388  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44389  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44390  * @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})
44391  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44392  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44393  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44394  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44395  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44396  * @cfg {String} title The title for the region (overrides panel titles)
44397  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44398  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44399  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44400  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44401  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44402  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44403  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44404  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44405  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44406  * @cfg {Boolean} showPin True to show a pin button
44407 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44408 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44409 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44410 * @cfg {Number} width  For East/West panels
44411 * @cfg {Number} height For North/South panels
44412 * @cfg {Boolean} split To show the splitter
44413  */
44414 Roo.LayoutRegion = function(mgr, config, pos){
44415     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44416     var dh = Roo.DomHelper;
44417     /** This region's container element 
44418     * @type Roo.Element */
44419     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44420     /** This region's title element 
44421     * @type Roo.Element */
44422
44423     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44424         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44425         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44426     ]}, true);
44427     this.titleEl.enableDisplayMode();
44428     /** This region's title text element 
44429     * @type HTMLElement */
44430     this.titleTextEl = this.titleEl.dom.firstChild;
44431     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44432     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44433     this.closeBtn.enableDisplayMode();
44434     this.closeBtn.on("click", this.closeClicked, this);
44435     this.closeBtn.hide();
44436
44437     this.createBody(config);
44438     this.visible = true;
44439     this.collapsed = false;
44440
44441     if(config.hideWhenEmpty){
44442         this.hide();
44443         this.on("paneladded", this.validateVisibility, this);
44444         this.on("panelremoved", this.validateVisibility, this);
44445     }
44446     this.applyConfig(config);
44447 };
44448
44449 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44450
44451     createBody : function(){
44452         /** This region's body element 
44453         * @type Roo.Element */
44454         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44455     },
44456
44457     applyConfig : function(c){
44458         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44459             var dh = Roo.DomHelper;
44460             if(c.titlebar !== false){
44461                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44462                 this.collapseBtn.on("click", this.collapse, this);
44463                 this.collapseBtn.enableDisplayMode();
44464
44465                 if(c.showPin === true || this.showPin){
44466                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44467                     this.stickBtn.enableDisplayMode();
44468                     this.stickBtn.on("click", this.expand, this);
44469                     this.stickBtn.hide();
44470                 }
44471             }
44472             /** This region's collapsed element
44473             * @type Roo.Element */
44474             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44475                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44476             ]}, true);
44477             if(c.floatable !== false){
44478                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44479                this.collapsedEl.on("click", this.collapseClick, this);
44480             }
44481
44482             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44483                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44484                    id: "message", unselectable: "on", style:{"float":"left"}});
44485                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44486              }
44487             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44488             this.expandBtn.on("click", this.expand, this);
44489         }
44490         if(this.collapseBtn){
44491             this.collapseBtn.setVisible(c.collapsible == true);
44492         }
44493         this.cmargins = c.cmargins || this.cmargins ||
44494                          (this.position == "west" || this.position == "east" ?
44495                              {top: 0, left: 2, right:2, bottom: 0} :
44496                              {top: 2, left: 0, right:0, bottom: 2});
44497         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44498         this.bottomTabs = c.tabPosition != "top";
44499         this.autoScroll = c.autoScroll || false;
44500         if(this.autoScroll){
44501             this.bodyEl.setStyle("overflow", "auto");
44502         }else{
44503             this.bodyEl.setStyle("overflow", "hidden");
44504         }
44505         //if(c.titlebar !== false){
44506             if((!c.titlebar && !c.title) || c.titlebar === false){
44507                 this.titleEl.hide();
44508             }else{
44509                 this.titleEl.show();
44510                 if(c.title){
44511                     this.titleTextEl.innerHTML = c.title;
44512                 }
44513             }
44514         //}
44515         this.duration = c.duration || .30;
44516         this.slideDuration = c.slideDuration || .45;
44517         this.config = c;
44518         if(c.collapsed){
44519             this.collapse(true);
44520         }
44521         if(c.hidden){
44522             this.hide();
44523         }
44524     },
44525     /**
44526      * Returns true if this region is currently visible.
44527      * @return {Boolean}
44528      */
44529     isVisible : function(){
44530         return this.visible;
44531     },
44532
44533     /**
44534      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44535      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44536      */
44537     setCollapsedTitle : function(title){
44538         title = title || "&#160;";
44539         if(this.collapsedTitleTextEl){
44540             this.collapsedTitleTextEl.innerHTML = title;
44541         }
44542     },
44543
44544     getBox : function(){
44545         var b;
44546         if(!this.collapsed){
44547             b = this.el.getBox(false, true);
44548         }else{
44549             b = this.collapsedEl.getBox(false, true);
44550         }
44551         return b;
44552     },
44553
44554     getMargins : function(){
44555         return this.collapsed ? this.cmargins : this.margins;
44556     },
44557
44558     highlight : function(){
44559         this.el.addClass("x-layout-panel-dragover");
44560     },
44561
44562     unhighlight : function(){
44563         this.el.removeClass("x-layout-panel-dragover");
44564     },
44565
44566     updateBox : function(box){
44567         this.box = box;
44568         if(!this.collapsed){
44569             this.el.dom.style.left = box.x + "px";
44570             this.el.dom.style.top = box.y + "px";
44571             this.updateBody(box.width, box.height);
44572         }else{
44573             this.collapsedEl.dom.style.left = box.x + "px";
44574             this.collapsedEl.dom.style.top = box.y + "px";
44575             this.collapsedEl.setSize(box.width, box.height);
44576         }
44577         if(this.tabs){
44578             this.tabs.autoSizeTabs();
44579         }
44580     },
44581
44582     updateBody : function(w, h){
44583         if(w !== null){
44584             this.el.setWidth(w);
44585             w -= this.el.getBorderWidth("rl");
44586             if(this.config.adjustments){
44587                 w += this.config.adjustments[0];
44588             }
44589         }
44590         if(h !== null){
44591             this.el.setHeight(h);
44592             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44593             h -= this.el.getBorderWidth("tb");
44594             if(this.config.adjustments){
44595                 h += this.config.adjustments[1];
44596             }
44597             this.bodyEl.setHeight(h);
44598             if(this.tabs){
44599                 h = this.tabs.syncHeight(h);
44600             }
44601         }
44602         if(this.panelSize){
44603             w = w !== null ? w : this.panelSize.width;
44604             h = h !== null ? h : this.panelSize.height;
44605         }
44606         if(this.activePanel){
44607             var el = this.activePanel.getEl();
44608             w = w !== null ? w : el.getWidth();
44609             h = h !== null ? h : el.getHeight();
44610             this.panelSize = {width: w, height: h};
44611             this.activePanel.setSize(w, h);
44612         }
44613         if(Roo.isIE && this.tabs){
44614             this.tabs.el.repaint();
44615         }
44616     },
44617
44618     /**
44619      * Returns the container element for this region.
44620      * @return {Roo.Element}
44621      */
44622     getEl : function(){
44623         return this.el;
44624     },
44625
44626     /**
44627      * Hides this region.
44628      */
44629     hide : function(){
44630         if(!this.collapsed){
44631             this.el.dom.style.left = "-2000px";
44632             this.el.hide();
44633         }else{
44634             this.collapsedEl.dom.style.left = "-2000px";
44635             this.collapsedEl.hide();
44636         }
44637         this.visible = false;
44638         this.fireEvent("visibilitychange", this, false);
44639     },
44640
44641     /**
44642      * Shows this region if it was previously hidden.
44643      */
44644     show : function(){
44645         if(!this.collapsed){
44646             this.el.show();
44647         }else{
44648             this.collapsedEl.show();
44649         }
44650         this.visible = true;
44651         this.fireEvent("visibilitychange", this, true);
44652     },
44653
44654     closeClicked : function(){
44655         if(this.activePanel){
44656             this.remove(this.activePanel);
44657         }
44658     },
44659
44660     collapseClick : function(e){
44661         if(this.isSlid){
44662            e.stopPropagation();
44663            this.slideIn();
44664         }else{
44665            e.stopPropagation();
44666            this.slideOut();
44667         }
44668     },
44669
44670     /**
44671      * Collapses this region.
44672      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44673      */
44674     collapse : function(skipAnim){
44675         if(this.collapsed) return;
44676         this.collapsed = true;
44677         if(this.split){
44678             this.split.el.hide();
44679         }
44680         if(this.config.animate && skipAnim !== true){
44681             this.fireEvent("invalidated", this);
44682             this.animateCollapse();
44683         }else{
44684             this.el.setLocation(-20000,-20000);
44685             this.el.hide();
44686             this.collapsedEl.show();
44687             this.fireEvent("collapsed", this);
44688             this.fireEvent("invalidated", this);
44689         }
44690     },
44691
44692     animateCollapse : function(){
44693         // overridden
44694     },
44695
44696     /**
44697      * Expands this region if it was previously collapsed.
44698      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44699      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44700      */
44701     expand : function(e, skipAnim){
44702         if(e) e.stopPropagation();
44703         if(!this.collapsed || this.el.hasActiveFx()) return;
44704         if(this.isSlid){
44705             this.afterSlideIn();
44706             skipAnim = true;
44707         }
44708         this.collapsed = false;
44709         if(this.config.animate && skipAnim !== true){
44710             this.animateExpand();
44711         }else{
44712             this.el.show();
44713             if(this.split){
44714                 this.split.el.show();
44715             }
44716             this.collapsedEl.setLocation(-2000,-2000);
44717             this.collapsedEl.hide();
44718             this.fireEvent("invalidated", this);
44719             this.fireEvent("expanded", this);
44720         }
44721     },
44722
44723     animateExpand : function(){
44724         // overridden
44725     },
44726
44727     initTabs : function(){
44728         this.bodyEl.setStyle("overflow", "hidden");
44729         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44730             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44731             disableTooltips: this.config.disableTabTips
44732         });
44733         if(this.config.hideTabs){
44734             ts.stripWrap.setDisplayed(false);
44735         }
44736         this.tabs = ts;
44737         ts.resizeTabs = this.config.resizeTabs === true;
44738         ts.minTabWidth = this.config.minTabWidth || 40;
44739         ts.maxTabWidth = this.config.maxTabWidth || 250;
44740         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44741         ts.monitorResize = false;
44742         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44743         ts.bodyEl.addClass('x-layout-tabs-body');
44744         this.panels.each(this.initPanelAsTab, this);
44745     },
44746
44747     initPanelAsTab : function(panel){
44748         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44749                     this.config.closeOnTab && panel.isClosable());
44750         if(panel.tabTip !== undefined){
44751             ti.setTooltip(panel.tabTip);
44752         }
44753         ti.on("activate", function(){
44754               this.setActivePanel(panel);
44755         }, this);
44756         if(this.config.closeOnTab){
44757             ti.on("beforeclose", function(t, e){
44758                 e.cancel = true;
44759                 this.remove(panel);
44760             }, this);
44761         }
44762         return ti;
44763     },
44764
44765     updatePanelTitle : function(panel, title){
44766         if(this.activePanel == panel){
44767             this.updateTitle(title);
44768         }
44769         if(this.tabs){
44770             var ti = this.tabs.getTab(panel.getEl().id);
44771             ti.setText(title);
44772             if(panel.tabTip !== undefined){
44773                 ti.setTooltip(panel.tabTip);
44774             }
44775         }
44776     },
44777
44778     updateTitle : function(title){
44779         if(this.titleTextEl && !this.config.title){
44780             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44781         }
44782     },
44783
44784     setActivePanel : function(panel){
44785         panel = this.getPanel(panel);
44786         if(this.activePanel && this.activePanel != panel){
44787             this.activePanel.setActiveState(false);
44788         }
44789         this.activePanel = panel;
44790         panel.setActiveState(true);
44791         if(this.panelSize){
44792             panel.setSize(this.panelSize.width, this.panelSize.height);
44793         }
44794         if(this.closeBtn){
44795             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44796         }
44797         this.updateTitle(panel.getTitle());
44798         if(this.tabs){
44799             this.fireEvent("invalidated", this);
44800         }
44801         this.fireEvent("panelactivated", this, panel);
44802     },
44803
44804     /**
44805      * Shows the specified panel.
44806      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44807      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44808      */
44809     showPanel : function(panel){
44810         if(panel = this.getPanel(panel)){
44811             if(this.tabs){
44812                 var tab = this.tabs.getTab(panel.getEl().id);
44813                 if(tab.isHidden()){
44814                     this.tabs.unhideTab(tab.id);
44815                 }
44816                 tab.activate();
44817             }else{
44818                 this.setActivePanel(panel);
44819             }
44820         }
44821         return panel;
44822     },
44823
44824     /**
44825      * Get the active panel for this region.
44826      * @return {Roo.ContentPanel} The active panel or null
44827      */
44828     getActivePanel : function(){
44829         return this.activePanel;
44830     },
44831
44832     validateVisibility : function(){
44833         if(this.panels.getCount() < 1){
44834             this.updateTitle("&#160;");
44835             this.closeBtn.hide();
44836             this.hide();
44837         }else{
44838             if(!this.isVisible()){
44839                 this.show();
44840             }
44841         }
44842     },
44843
44844     /**
44845      * Adds the passed ContentPanel(s) to this region.
44846      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44847      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44848      */
44849     add : function(panel){
44850         if(arguments.length > 1){
44851             for(var i = 0, len = arguments.length; i < len; i++) {
44852                 this.add(arguments[i]);
44853             }
44854             return null;
44855         }
44856         if(this.hasPanel(panel)){
44857             this.showPanel(panel);
44858             return panel;
44859         }
44860         panel.setRegion(this);
44861         this.panels.add(panel);
44862         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44863             this.bodyEl.dom.appendChild(panel.getEl().dom);
44864             if(panel.background !== true){
44865                 this.setActivePanel(panel);
44866             }
44867             this.fireEvent("paneladded", this, panel);
44868             return panel;
44869         }
44870         if(!this.tabs){
44871             this.initTabs();
44872         }else{
44873             this.initPanelAsTab(panel);
44874         }
44875         if(panel.background !== true){
44876             this.tabs.activate(panel.getEl().id);
44877         }
44878         this.fireEvent("paneladded", this, panel);
44879         return panel;
44880     },
44881
44882     /**
44883      * Hides the tab for the specified panel.
44884      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44885      */
44886     hidePanel : function(panel){
44887         if(this.tabs && (panel = this.getPanel(panel))){
44888             this.tabs.hideTab(panel.getEl().id);
44889         }
44890     },
44891
44892     /**
44893      * Unhides the tab for a previously hidden panel.
44894      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44895      */
44896     unhidePanel : function(panel){
44897         if(this.tabs && (panel = this.getPanel(panel))){
44898             this.tabs.unhideTab(panel.getEl().id);
44899         }
44900     },
44901
44902     clearPanels : function(){
44903         while(this.panels.getCount() > 0){
44904              this.remove(this.panels.first());
44905         }
44906     },
44907
44908     /**
44909      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44910      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44911      * @param {Boolean} preservePanel Overrides the config preservePanel option
44912      * @return {Roo.ContentPanel} The panel that was removed
44913      */
44914     remove : function(panel, preservePanel){
44915         panel = this.getPanel(panel);
44916         if(!panel){
44917             return null;
44918         }
44919         var e = {};
44920         this.fireEvent("beforeremove", this, panel, e);
44921         if(e.cancel === true){
44922             return null;
44923         }
44924         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44925         var panelId = panel.getId();
44926         this.panels.removeKey(panelId);
44927         if(preservePanel){
44928             document.body.appendChild(panel.getEl().dom);
44929         }
44930         if(this.tabs){
44931             this.tabs.removeTab(panel.getEl().id);
44932         }else if (!preservePanel){
44933             this.bodyEl.dom.removeChild(panel.getEl().dom);
44934         }
44935         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44936             var p = this.panels.first();
44937             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44938             tempEl.appendChild(p.getEl().dom);
44939             this.bodyEl.update("");
44940             this.bodyEl.dom.appendChild(p.getEl().dom);
44941             tempEl = null;
44942             this.updateTitle(p.getTitle());
44943             this.tabs = null;
44944             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44945             this.setActivePanel(p);
44946         }
44947         panel.setRegion(null);
44948         if(this.activePanel == panel){
44949             this.activePanel = null;
44950         }
44951         if(this.config.autoDestroy !== false && preservePanel !== true){
44952             try{panel.destroy();}catch(e){}
44953         }
44954         this.fireEvent("panelremoved", this, panel);
44955         return panel;
44956     },
44957
44958     /**
44959      * Returns the TabPanel component used by this region
44960      * @return {Roo.TabPanel}
44961      */
44962     getTabs : function(){
44963         return this.tabs;
44964     },
44965
44966     createTool : function(parentEl, className){
44967         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44968             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44969         btn.addClassOnOver("x-layout-tools-button-over");
44970         return btn;
44971     }
44972 });/*
44973  * Based on:
44974  * Ext JS Library 1.1.1
44975  * Copyright(c) 2006-2007, Ext JS, LLC.
44976  *
44977  * Originally Released Under LGPL - original licence link has changed is not relivant.
44978  *
44979  * Fork - LGPL
44980  * <script type="text/javascript">
44981  */
44982  
44983
44984
44985 /**
44986  * @class Roo.SplitLayoutRegion
44987  * @extends Roo.LayoutRegion
44988  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44989  */
44990 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44991     this.cursor = cursor;
44992     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44993 };
44994
44995 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44996     splitTip : "Drag to resize.",
44997     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44998     useSplitTips : false,
44999
45000     applyConfig : function(config){
45001         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45002         if(config.split){
45003             if(!this.split){
45004                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45005                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45006                 /** The SplitBar for this region 
45007                 * @type Roo.SplitBar */
45008                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45009                 this.split.on("moved", this.onSplitMove, this);
45010                 this.split.useShim = config.useShim === true;
45011                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45012                 if(this.useSplitTips){
45013                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45014                 }
45015                 if(config.collapsible){
45016                     this.split.el.on("dblclick", this.collapse,  this);
45017                 }
45018             }
45019             if(typeof config.minSize != "undefined"){
45020                 this.split.minSize = config.minSize;
45021             }
45022             if(typeof config.maxSize != "undefined"){
45023                 this.split.maxSize = config.maxSize;
45024             }
45025             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45026                 this.hideSplitter();
45027             }
45028         }
45029     },
45030
45031     getHMaxSize : function(){
45032          var cmax = this.config.maxSize || 10000;
45033          var center = this.mgr.getRegion("center");
45034          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45035     },
45036
45037     getVMaxSize : function(){
45038          var cmax = this.config.maxSize || 10000;
45039          var center = this.mgr.getRegion("center");
45040          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45041     },
45042
45043     onSplitMove : function(split, newSize){
45044         this.fireEvent("resized", this, newSize);
45045     },
45046     
45047     /** 
45048      * Returns the {@link Roo.SplitBar} for this region.
45049      * @return {Roo.SplitBar}
45050      */
45051     getSplitBar : function(){
45052         return this.split;
45053     },
45054     
45055     hide : function(){
45056         this.hideSplitter();
45057         Roo.SplitLayoutRegion.superclass.hide.call(this);
45058     },
45059
45060     hideSplitter : function(){
45061         if(this.split){
45062             this.split.el.setLocation(-2000,-2000);
45063             this.split.el.hide();
45064         }
45065     },
45066
45067     show : function(){
45068         if(this.split){
45069             this.split.el.show();
45070         }
45071         Roo.SplitLayoutRegion.superclass.show.call(this);
45072     },
45073     
45074     beforeSlide: function(){
45075         if(Roo.isGecko){// firefox overflow auto bug workaround
45076             this.bodyEl.clip();
45077             if(this.tabs) this.tabs.bodyEl.clip();
45078             if(this.activePanel){
45079                 this.activePanel.getEl().clip();
45080                 
45081                 if(this.activePanel.beforeSlide){
45082                     this.activePanel.beforeSlide();
45083                 }
45084             }
45085         }
45086     },
45087     
45088     afterSlide : function(){
45089         if(Roo.isGecko){// firefox overflow auto bug workaround
45090             this.bodyEl.unclip();
45091             if(this.tabs) this.tabs.bodyEl.unclip();
45092             if(this.activePanel){
45093                 this.activePanel.getEl().unclip();
45094                 if(this.activePanel.afterSlide){
45095                     this.activePanel.afterSlide();
45096                 }
45097             }
45098         }
45099     },
45100
45101     initAutoHide : function(){
45102         if(this.autoHide !== false){
45103             if(!this.autoHideHd){
45104                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45105                 this.autoHideHd = {
45106                     "mouseout": function(e){
45107                         if(!e.within(this.el, true)){
45108                             st.delay(500);
45109                         }
45110                     },
45111                     "mouseover" : function(e){
45112                         st.cancel();
45113                     },
45114                     scope : this
45115                 };
45116             }
45117             this.el.on(this.autoHideHd);
45118         }
45119     },
45120
45121     clearAutoHide : function(){
45122         if(this.autoHide !== false){
45123             this.el.un("mouseout", this.autoHideHd.mouseout);
45124             this.el.un("mouseover", this.autoHideHd.mouseover);
45125         }
45126     },
45127
45128     clearMonitor : function(){
45129         Roo.get(document).un("click", this.slideInIf, this);
45130     },
45131
45132     // these names are backwards but not changed for compat
45133     slideOut : function(){
45134         if(this.isSlid || this.el.hasActiveFx()){
45135             return;
45136         }
45137         this.isSlid = true;
45138         if(this.collapseBtn){
45139             this.collapseBtn.hide();
45140         }
45141         this.closeBtnState = this.closeBtn.getStyle('display');
45142         this.closeBtn.hide();
45143         if(this.stickBtn){
45144             this.stickBtn.show();
45145         }
45146         this.el.show();
45147         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45148         this.beforeSlide();
45149         this.el.setStyle("z-index", 10001);
45150         this.el.slideIn(this.getSlideAnchor(), {
45151             callback: function(){
45152                 this.afterSlide();
45153                 this.initAutoHide();
45154                 Roo.get(document).on("click", this.slideInIf, this);
45155                 this.fireEvent("slideshow", this);
45156             },
45157             scope: this,
45158             block: true
45159         });
45160     },
45161
45162     afterSlideIn : function(){
45163         this.clearAutoHide();
45164         this.isSlid = false;
45165         this.clearMonitor();
45166         this.el.setStyle("z-index", "");
45167         if(this.collapseBtn){
45168             this.collapseBtn.show();
45169         }
45170         this.closeBtn.setStyle('display', this.closeBtnState);
45171         if(this.stickBtn){
45172             this.stickBtn.hide();
45173         }
45174         this.fireEvent("slidehide", this);
45175     },
45176
45177     slideIn : function(cb){
45178         if(!this.isSlid || this.el.hasActiveFx()){
45179             Roo.callback(cb);
45180             return;
45181         }
45182         this.isSlid = false;
45183         this.beforeSlide();
45184         this.el.slideOut(this.getSlideAnchor(), {
45185             callback: function(){
45186                 this.el.setLeftTop(-10000, -10000);
45187                 this.afterSlide();
45188                 this.afterSlideIn();
45189                 Roo.callback(cb);
45190             },
45191             scope: this,
45192             block: true
45193         });
45194     },
45195     
45196     slideInIf : function(e){
45197         if(!e.within(this.el)){
45198             this.slideIn();
45199         }
45200     },
45201
45202     animateCollapse : function(){
45203         this.beforeSlide();
45204         this.el.setStyle("z-index", 20000);
45205         var anchor = this.getSlideAnchor();
45206         this.el.slideOut(anchor, {
45207             callback : function(){
45208                 this.el.setStyle("z-index", "");
45209                 this.collapsedEl.slideIn(anchor, {duration:.3});
45210                 this.afterSlide();
45211                 this.el.setLocation(-10000,-10000);
45212                 this.el.hide();
45213                 this.fireEvent("collapsed", this);
45214             },
45215             scope: this,
45216             block: true
45217         });
45218     },
45219
45220     animateExpand : function(){
45221         this.beforeSlide();
45222         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45223         this.el.setStyle("z-index", 20000);
45224         this.collapsedEl.hide({
45225             duration:.1
45226         });
45227         this.el.slideIn(this.getSlideAnchor(), {
45228             callback : function(){
45229                 this.el.setStyle("z-index", "");
45230                 this.afterSlide();
45231                 if(this.split){
45232                     this.split.el.show();
45233                 }
45234                 this.fireEvent("invalidated", this);
45235                 this.fireEvent("expanded", this);
45236             },
45237             scope: this,
45238             block: true
45239         });
45240     },
45241
45242     anchors : {
45243         "west" : "left",
45244         "east" : "right",
45245         "north" : "top",
45246         "south" : "bottom"
45247     },
45248
45249     sanchors : {
45250         "west" : "l",
45251         "east" : "r",
45252         "north" : "t",
45253         "south" : "b"
45254     },
45255
45256     canchors : {
45257         "west" : "tl-tr",
45258         "east" : "tr-tl",
45259         "north" : "tl-bl",
45260         "south" : "bl-tl"
45261     },
45262
45263     getAnchor : function(){
45264         return this.anchors[this.position];
45265     },
45266
45267     getCollapseAnchor : function(){
45268         return this.canchors[this.position];
45269     },
45270
45271     getSlideAnchor : function(){
45272         return this.sanchors[this.position];
45273     },
45274
45275     getAlignAdj : function(){
45276         var cm = this.cmargins;
45277         switch(this.position){
45278             case "west":
45279                 return [0, 0];
45280             break;
45281             case "east":
45282                 return [0, 0];
45283             break;
45284             case "north":
45285                 return [0, 0];
45286             break;
45287             case "south":
45288                 return [0, 0];
45289             break;
45290         }
45291     },
45292
45293     getExpandAdj : function(){
45294         var c = this.collapsedEl, cm = this.cmargins;
45295         switch(this.position){
45296             case "west":
45297                 return [-(cm.right+c.getWidth()+cm.left), 0];
45298             break;
45299             case "east":
45300                 return [cm.right+c.getWidth()+cm.left, 0];
45301             break;
45302             case "north":
45303                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45304             break;
45305             case "south":
45306                 return [0, cm.top+cm.bottom+c.getHeight()];
45307             break;
45308         }
45309     }
45310 });/*
45311  * Based on:
45312  * Ext JS Library 1.1.1
45313  * Copyright(c) 2006-2007, Ext JS, LLC.
45314  *
45315  * Originally Released Under LGPL - original licence link has changed is not relivant.
45316  *
45317  * Fork - LGPL
45318  * <script type="text/javascript">
45319  */
45320 /*
45321  * These classes are private internal classes
45322  */
45323 Roo.CenterLayoutRegion = function(mgr, config){
45324     Roo.LayoutRegion.call(this, mgr, config, "center");
45325     this.visible = true;
45326     this.minWidth = config.minWidth || 20;
45327     this.minHeight = config.minHeight || 20;
45328 };
45329
45330 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45331     hide : function(){
45332         // center panel can't be hidden
45333     },
45334     
45335     show : function(){
45336         // center panel can't be hidden
45337     },
45338     
45339     getMinWidth: function(){
45340         return this.minWidth;
45341     },
45342     
45343     getMinHeight: function(){
45344         return this.minHeight;
45345     }
45346 });
45347
45348
45349 Roo.NorthLayoutRegion = function(mgr, config){
45350     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45351     if(this.split){
45352         this.split.placement = Roo.SplitBar.TOP;
45353         this.split.orientation = Roo.SplitBar.VERTICAL;
45354         this.split.el.addClass("x-layout-split-v");
45355     }
45356     var size = config.initialSize || config.height;
45357     if(typeof size != "undefined"){
45358         this.el.setHeight(size);
45359     }
45360 };
45361 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45362     orientation: Roo.SplitBar.VERTICAL,
45363     getBox : function(){
45364         if(this.collapsed){
45365             return this.collapsedEl.getBox();
45366         }
45367         var box = this.el.getBox();
45368         if(this.split){
45369             box.height += this.split.el.getHeight();
45370         }
45371         return box;
45372     },
45373     
45374     updateBox : function(box){
45375         if(this.split && !this.collapsed){
45376             box.height -= this.split.el.getHeight();
45377             this.split.el.setLeft(box.x);
45378             this.split.el.setTop(box.y+box.height);
45379             this.split.el.setWidth(box.width);
45380         }
45381         if(this.collapsed){
45382             this.updateBody(box.width, null);
45383         }
45384         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45385     }
45386 });
45387
45388 Roo.SouthLayoutRegion = function(mgr, config){
45389     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45390     if(this.split){
45391         this.split.placement = Roo.SplitBar.BOTTOM;
45392         this.split.orientation = Roo.SplitBar.VERTICAL;
45393         this.split.el.addClass("x-layout-split-v");
45394     }
45395     var size = config.initialSize || config.height;
45396     if(typeof size != "undefined"){
45397         this.el.setHeight(size);
45398     }
45399 };
45400 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45401     orientation: Roo.SplitBar.VERTICAL,
45402     getBox : function(){
45403         if(this.collapsed){
45404             return this.collapsedEl.getBox();
45405         }
45406         var box = this.el.getBox();
45407         if(this.split){
45408             var sh = this.split.el.getHeight();
45409             box.height += sh;
45410             box.y -= sh;
45411         }
45412         return box;
45413     },
45414     
45415     updateBox : function(box){
45416         if(this.split && !this.collapsed){
45417             var sh = this.split.el.getHeight();
45418             box.height -= sh;
45419             box.y += sh;
45420             this.split.el.setLeft(box.x);
45421             this.split.el.setTop(box.y-sh);
45422             this.split.el.setWidth(box.width);
45423         }
45424         if(this.collapsed){
45425             this.updateBody(box.width, null);
45426         }
45427         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45428     }
45429 });
45430
45431 Roo.EastLayoutRegion = function(mgr, config){
45432     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45433     if(this.split){
45434         this.split.placement = Roo.SplitBar.RIGHT;
45435         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45436         this.split.el.addClass("x-layout-split-h");
45437     }
45438     var size = config.initialSize || config.width;
45439     if(typeof size != "undefined"){
45440         this.el.setWidth(size);
45441     }
45442 };
45443 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45444     orientation: Roo.SplitBar.HORIZONTAL,
45445     getBox : function(){
45446         if(this.collapsed){
45447             return this.collapsedEl.getBox();
45448         }
45449         var box = this.el.getBox();
45450         if(this.split){
45451             var sw = this.split.el.getWidth();
45452             box.width += sw;
45453             box.x -= sw;
45454         }
45455         return box;
45456     },
45457
45458     updateBox : function(box){
45459         if(this.split && !this.collapsed){
45460             var sw = this.split.el.getWidth();
45461             box.width -= sw;
45462             this.split.el.setLeft(box.x);
45463             this.split.el.setTop(box.y);
45464             this.split.el.setHeight(box.height);
45465             box.x += sw;
45466         }
45467         if(this.collapsed){
45468             this.updateBody(null, box.height);
45469         }
45470         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45471     }
45472 });
45473
45474 Roo.WestLayoutRegion = function(mgr, config){
45475     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45476     if(this.split){
45477         this.split.placement = Roo.SplitBar.LEFT;
45478         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45479         this.split.el.addClass("x-layout-split-h");
45480     }
45481     var size = config.initialSize || config.width;
45482     if(typeof size != "undefined"){
45483         this.el.setWidth(size);
45484     }
45485 };
45486 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45487     orientation: Roo.SplitBar.HORIZONTAL,
45488     getBox : function(){
45489         if(this.collapsed){
45490             return this.collapsedEl.getBox();
45491         }
45492         var box = this.el.getBox();
45493         if(this.split){
45494             box.width += this.split.el.getWidth();
45495         }
45496         return box;
45497     },
45498     
45499     updateBox : function(box){
45500         if(this.split && !this.collapsed){
45501             var sw = this.split.el.getWidth();
45502             box.width -= sw;
45503             this.split.el.setLeft(box.x+box.width);
45504             this.split.el.setTop(box.y);
45505             this.split.el.setHeight(box.height);
45506         }
45507         if(this.collapsed){
45508             this.updateBody(null, box.height);
45509         }
45510         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45511     }
45512 });
45513 /*
45514  * Based on:
45515  * Ext JS Library 1.1.1
45516  * Copyright(c) 2006-2007, Ext JS, LLC.
45517  *
45518  * Originally Released Under LGPL - original licence link has changed is not relivant.
45519  *
45520  * Fork - LGPL
45521  * <script type="text/javascript">
45522  */
45523  
45524  
45525 /*
45526  * Private internal class for reading and applying state
45527  */
45528 Roo.LayoutStateManager = function(layout){
45529      // default empty state
45530      this.state = {
45531         north: {},
45532         south: {},
45533         east: {},
45534         west: {}       
45535     };
45536 };
45537
45538 Roo.LayoutStateManager.prototype = {
45539     init : function(layout, provider){
45540         this.provider = provider;
45541         var state = provider.get(layout.id+"-layout-state");
45542         if(state){
45543             var wasUpdating = layout.isUpdating();
45544             if(!wasUpdating){
45545                 layout.beginUpdate();
45546             }
45547             for(var key in state){
45548                 if(typeof state[key] != "function"){
45549                     var rstate = state[key];
45550                     var r = layout.getRegion(key);
45551                     if(r && rstate){
45552                         if(rstate.size){
45553                             r.resizeTo(rstate.size);
45554                         }
45555                         if(rstate.collapsed == true){
45556                             r.collapse(true);
45557                         }else{
45558                             r.expand(null, true);
45559                         }
45560                     }
45561                 }
45562             }
45563             if(!wasUpdating){
45564                 layout.endUpdate();
45565             }
45566             this.state = state; 
45567         }
45568         this.layout = layout;
45569         layout.on("regionresized", this.onRegionResized, this);
45570         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45571         layout.on("regionexpanded", this.onRegionExpanded, this);
45572     },
45573     
45574     storeState : function(){
45575         this.provider.set(this.layout.id+"-layout-state", this.state);
45576     },
45577     
45578     onRegionResized : function(region, newSize){
45579         this.state[region.getPosition()].size = newSize;
45580         this.storeState();
45581     },
45582     
45583     onRegionCollapsed : function(region){
45584         this.state[region.getPosition()].collapsed = true;
45585         this.storeState();
45586     },
45587     
45588     onRegionExpanded : function(region){
45589         this.state[region.getPosition()].collapsed = false;
45590         this.storeState();
45591     }
45592 };/*
45593  * Based on:
45594  * Ext JS Library 1.1.1
45595  * Copyright(c) 2006-2007, Ext JS, LLC.
45596  *
45597  * Originally Released Under LGPL - original licence link has changed is not relivant.
45598  *
45599  * Fork - LGPL
45600  * <script type="text/javascript">
45601  */
45602 /**
45603  * @class Roo.ContentPanel
45604  * @extends Roo.util.Observable
45605  * A basic ContentPanel element.
45606  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45607  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45608  * @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
45609  * @cfg {Boolean} closable True if the panel can be closed/removed
45610  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45611  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45612  * @cfg {Toolbar} toolbar A toolbar for this panel
45613  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45614  * @cfg {String} title The title for this panel
45615  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45616  * @cfg {String} url Calls {@link #setUrl} with this value
45617  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45618  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45619  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45620  * @constructor
45621  * Create a new ContentPanel.
45622  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45623  * @param {String/Object} config A string to set only the title or a config object
45624  * @param {String} content (optional) Set the HTML content for this panel
45625  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45626  */
45627 Roo.ContentPanel = function(el, config, content){
45628     
45629      
45630     /*
45631     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45632         config = el;
45633         el = Roo.id();
45634     }
45635     if (config && config.parentLayout) { 
45636         el = config.parentLayout.el.createChild(); 
45637     }
45638     */
45639     if(el.autoCreate){ // xtype is available if this is called from factory
45640         config = el;
45641         el = Roo.id();
45642     }
45643     this.el = Roo.get(el);
45644     if(!this.el && config && config.autoCreate){
45645         if(typeof config.autoCreate == "object"){
45646             if(!config.autoCreate.id){
45647                 config.autoCreate.id = config.id||el;
45648             }
45649             this.el = Roo.DomHelper.append(document.body,
45650                         config.autoCreate, true);
45651         }else{
45652             this.el = Roo.DomHelper.append(document.body,
45653                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45654         }
45655     }
45656     this.closable = false;
45657     this.loaded = false;
45658     this.active = false;
45659     if(typeof config == "string"){
45660         this.title = config;
45661     }else{
45662         Roo.apply(this, config);
45663     }
45664     
45665     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45666         this.wrapEl = this.el.wrap();    
45667         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45668         
45669     }
45670     
45671     
45672     
45673     if(this.resizeEl){
45674         this.resizeEl = Roo.get(this.resizeEl, true);
45675     }else{
45676         this.resizeEl = this.el;
45677     }
45678     this.addEvents({
45679         /**
45680          * @event activate
45681          * Fires when this panel is activated. 
45682          * @param {Roo.ContentPanel} this
45683          */
45684         "activate" : true,
45685         /**
45686          * @event deactivate
45687          * Fires when this panel is activated. 
45688          * @param {Roo.ContentPanel} this
45689          */
45690         "deactivate" : true,
45691
45692         /**
45693          * @event resize
45694          * Fires when this panel is resized if fitToFrame is true.
45695          * @param {Roo.ContentPanel} this
45696          * @param {Number} width The width after any component adjustments
45697          * @param {Number} height The height after any component adjustments
45698          */
45699         "resize" : true
45700     });
45701     if(this.autoScroll){
45702         this.resizeEl.setStyle("overflow", "auto");
45703     } else {
45704         // fix randome scrolling
45705         this.el.on('scroll', function() {
45706             Roo.log('fix random scolling');
45707             this.scrollTo('top',0); 
45708         });
45709     }
45710     content = content || this.content;
45711     if(content){
45712         this.setContent(content);
45713     }
45714     if(config && config.url){
45715         this.setUrl(this.url, this.params, this.loadOnce);
45716     }
45717     
45718     
45719     
45720     Roo.ContentPanel.superclass.constructor.call(this);
45721 };
45722
45723 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45724     tabTip:'',
45725     setRegion : function(region){
45726         this.region = region;
45727         if(region){
45728            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45729         }else{
45730            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45731         } 
45732     },
45733     
45734     /**
45735      * Returns the toolbar for this Panel if one was configured. 
45736      * @return {Roo.Toolbar} 
45737      */
45738     getToolbar : function(){
45739         return this.toolbar;
45740     },
45741     
45742     setActiveState : function(active){
45743         this.active = active;
45744         if(!active){
45745             this.fireEvent("deactivate", this);
45746         }else{
45747             this.fireEvent("activate", this);
45748         }
45749     },
45750     /**
45751      * Updates this panel's element
45752      * @param {String} content The new content
45753      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45754     */
45755     setContent : function(content, loadScripts){
45756         this.el.update(content, loadScripts);
45757     },
45758
45759     ignoreResize : function(w, h){
45760         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45761             return true;
45762         }else{
45763             this.lastSize = {width: w, height: h};
45764             return false;
45765         }
45766     },
45767     /**
45768      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45769      * @return {Roo.UpdateManager} The UpdateManager
45770      */
45771     getUpdateManager : function(){
45772         return this.el.getUpdateManager();
45773     },
45774      /**
45775      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45776      * @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:
45777 <pre><code>
45778 panel.load({
45779     url: "your-url.php",
45780     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45781     callback: yourFunction,
45782     scope: yourObject, //(optional scope)
45783     discardUrl: false,
45784     nocache: false,
45785     text: "Loading...",
45786     timeout: 30,
45787     scripts: false
45788 });
45789 </code></pre>
45790      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45791      * 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.
45792      * @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}
45793      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45794      * @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.
45795      * @return {Roo.ContentPanel} this
45796      */
45797     load : function(){
45798         var um = this.el.getUpdateManager();
45799         um.update.apply(um, arguments);
45800         return this;
45801     },
45802
45803
45804     /**
45805      * 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.
45806      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45807      * @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)
45808      * @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)
45809      * @return {Roo.UpdateManager} The UpdateManager
45810      */
45811     setUrl : function(url, params, loadOnce){
45812         if(this.refreshDelegate){
45813             this.removeListener("activate", this.refreshDelegate);
45814         }
45815         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45816         this.on("activate", this.refreshDelegate);
45817         return this.el.getUpdateManager();
45818     },
45819     
45820     _handleRefresh : function(url, params, loadOnce){
45821         if(!loadOnce || !this.loaded){
45822             var updater = this.el.getUpdateManager();
45823             updater.update(url, params, this._setLoaded.createDelegate(this));
45824         }
45825     },
45826     
45827     _setLoaded : function(){
45828         this.loaded = true;
45829     }, 
45830     
45831     /**
45832      * Returns this panel's id
45833      * @return {String} 
45834      */
45835     getId : function(){
45836         return this.el.id;
45837     },
45838     
45839     /** 
45840      * Returns this panel's element - used by regiosn to add.
45841      * @return {Roo.Element} 
45842      */
45843     getEl : function(){
45844         return this.wrapEl || this.el;
45845     },
45846     
45847     adjustForComponents : function(width, height){
45848         if(this.resizeEl != this.el){
45849             width -= this.el.getFrameWidth('lr');
45850             height -= this.el.getFrameWidth('tb');
45851         }
45852         if(this.toolbar){
45853             var te = this.toolbar.getEl();
45854             height -= te.getHeight();
45855             te.setWidth(width);
45856         }
45857         if(this.adjustments){
45858             width += this.adjustments[0];
45859             height += this.adjustments[1];
45860         }
45861         return {"width": width, "height": height};
45862     },
45863     
45864     setSize : function(width, height){
45865         if(this.fitToFrame && !this.ignoreResize(width, height)){
45866             if(this.fitContainer && this.resizeEl != this.el){
45867                 this.el.setSize(width, height);
45868             }
45869             var size = this.adjustForComponents(width, height);
45870             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45871             this.fireEvent('resize', this, size.width, size.height);
45872         }
45873     },
45874     
45875     /**
45876      * Returns this panel's title
45877      * @return {String} 
45878      */
45879     getTitle : function(){
45880         return this.title;
45881     },
45882     
45883     /**
45884      * Set this panel's title
45885      * @param {String} title
45886      */
45887     setTitle : function(title){
45888         this.title = title;
45889         if(this.region){
45890             this.region.updatePanelTitle(this, title);
45891         }
45892     },
45893     
45894     /**
45895      * Returns true is this panel was configured to be closable
45896      * @return {Boolean} 
45897      */
45898     isClosable : function(){
45899         return this.closable;
45900     },
45901     
45902     beforeSlide : function(){
45903         this.el.clip();
45904         this.resizeEl.clip();
45905     },
45906     
45907     afterSlide : function(){
45908         this.el.unclip();
45909         this.resizeEl.unclip();
45910     },
45911     
45912     /**
45913      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45914      *   Will fail silently if the {@link #setUrl} method has not been called.
45915      *   This does not activate the panel, just updates its content.
45916      */
45917     refresh : function(){
45918         if(this.refreshDelegate){
45919            this.loaded = false;
45920            this.refreshDelegate();
45921         }
45922     },
45923     
45924     /**
45925      * Destroys this panel
45926      */
45927     destroy : function(){
45928         this.el.removeAllListeners();
45929         var tempEl = document.createElement("span");
45930         tempEl.appendChild(this.el.dom);
45931         tempEl.innerHTML = "";
45932         this.el.remove();
45933         this.el = null;
45934     },
45935     
45936       /**
45937      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45938      * <pre><code>
45939
45940 layout.addxtype({
45941        xtype : 'Form',
45942        items: [ .... ]
45943    }
45944 );
45945
45946 </code></pre>
45947      * @param {Object} cfg Xtype definition of item to add.
45948      */
45949     
45950     addxtype : function(cfg) {
45951         // add form..
45952         if (cfg.xtype.match(/^Form$/)) {
45953             var el = this.el.createChild();
45954
45955             this.form = new  Roo.form.Form(cfg);
45956             
45957             
45958             if ( this.form.allItems.length) this.form.render(el.dom);
45959             return this.form;
45960         }
45961         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45962             // views..
45963             cfg.el = this.el.appendChild(document.createElement("div"));
45964             // factory?
45965             var ret = new Roo[cfg.xtype](cfg);
45966             ret.render(false, ''); // render blank..
45967             return ret;
45968             
45969         }
45970         return false;
45971         
45972     }
45973 });
45974
45975 /**
45976  * @class Roo.GridPanel
45977  * @extends Roo.ContentPanel
45978  * @constructor
45979  * Create a new GridPanel.
45980  * @param {Roo.grid.Grid} grid The grid for this panel
45981  * @param {String/Object} config A string to set only the panel's title, or a config object
45982  */
45983 Roo.GridPanel = function(grid, config){
45984     
45985   
45986     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45987         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45988         
45989     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45990     
45991     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45992     
45993     if(this.toolbar){
45994         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45995     }
45996     // xtype created footer. - not sure if will work as we normally have to render first..
45997     if (this.footer && !this.footer.el && this.footer.xtype) {
45998         
45999         this.footer.container = this.grid.getView().getFooterPanel(true);
46000         this.footer.dataSource = this.grid.dataSource;
46001         this.footer = Roo.factory(this.footer, Roo);
46002         
46003     }
46004     
46005     grid.monitorWindowResize = false; // turn off autosizing
46006     grid.autoHeight = false;
46007     grid.autoWidth = false;
46008     this.grid = grid;
46009     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46010 };
46011
46012 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46013     getId : function(){
46014         return this.grid.id;
46015     },
46016     
46017     /**
46018      * Returns the grid for this panel
46019      * @return {Roo.grid.Grid} 
46020      */
46021     getGrid : function(){
46022         return this.grid;    
46023     },
46024     
46025     setSize : function(width, height){
46026         if(!this.ignoreResize(width, height)){
46027             var grid = this.grid;
46028             var size = this.adjustForComponents(width, height);
46029             grid.getGridEl().setSize(size.width, size.height);
46030             grid.autoSize();
46031         }
46032     },
46033     
46034     beforeSlide : function(){
46035         this.grid.getView().scroller.clip();
46036     },
46037     
46038     afterSlide : function(){
46039         this.grid.getView().scroller.unclip();
46040     },
46041     
46042     destroy : function(){
46043         this.grid.destroy();
46044         delete this.grid;
46045         Roo.GridPanel.superclass.destroy.call(this); 
46046     }
46047 });
46048
46049
46050 /**
46051  * @class Roo.NestedLayoutPanel
46052  * @extends Roo.ContentPanel
46053  * @constructor
46054  * Create a new NestedLayoutPanel.
46055  * 
46056  * 
46057  * @param {Roo.BorderLayout} layout The layout for this panel
46058  * @param {String/Object} config A string to set only the title or a config object
46059  */
46060 Roo.NestedLayoutPanel = function(layout, config)
46061 {
46062     // construct with only one argument..
46063     /* FIXME - implement nicer consturctors
46064     if (layout.layout) {
46065         config = layout;
46066         layout = config.layout;
46067         delete config.layout;
46068     }
46069     if (layout.xtype && !layout.getEl) {
46070         // then layout needs constructing..
46071         layout = Roo.factory(layout, Roo);
46072     }
46073     */
46074     
46075     
46076     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46077     
46078     layout.monitorWindowResize = false; // turn off autosizing
46079     this.layout = layout;
46080     this.layout.getEl().addClass("x-layout-nested-layout");
46081     
46082     
46083     
46084     
46085 };
46086
46087 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46088
46089     setSize : function(width, height){
46090         if(!this.ignoreResize(width, height)){
46091             var size = this.adjustForComponents(width, height);
46092             var el = this.layout.getEl();
46093             el.setSize(size.width, size.height);
46094             var touch = el.dom.offsetWidth;
46095             this.layout.layout();
46096             // ie requires a double layout on the first pass
46097             if(Roo.isIE && !this.initialized){
46098                 this.initialized = true;
46099                 this.layout.layout();
46100             }
46101         }
46102     },
46103     
46104     // activate all subpanels if not currently active..
46105     
46106     setActiveState : function(active){
46107         this.active = active;
46108         if(!active){
46109             this.fireEvent("deactivate", this);
46110             return;
46111         }
46112         
46113         this.fireEvent("activate", this);
46114         // not sure if this should happen before or after..
46115         if (!this.layout) {
46116             return; // should not happen..
46117         }
46118         var reg = false;
46119         for (var r in this.layout.regions) {
46120             reg = this.layout.getRegion(r);
46121             if (reg.getActivePanel()) {
46122                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46123                 reg.setActivePanel(reg.getActivePanel());
46124                 continue;
46125             }
46126             if (!reg.panels.length) {
46127                 continue;
46128             }
46129             reg.showPanel(reg.getPanel(0));
46130         }
46131         
46132         
46133         
46134         
46135     },
46136     
46137     /**
46138      * Returns the nested BorderLayout for this panel
46139      * @return {Roo.BorderLayout} 
46140      */
46141     getLayout : function(){
46142         return this.layout;
46143     },
46144     
46145      /**
46146      * Adds a xtype elements to the layout of the nested panel
46147      * <pre><code>
46148
46149 panel.addxtype({
46150        xtype : 'ContentPanel',
46151        region: 'west',
46152        items: [ .... ]
46153    }
46154 );
46155
46156 panel.addxtype({
46157         xtype : 'NestedLayoutPanel',
46158         region: 'west',
46159         layout: {
46160            center: { },
46161            west: { }   
46162         },
46163         items : [ ... list of content panels or nested layout panels.. ]
46164    }
46165 );
46166 </code></pre>
46167      * @param {Object} cfg Xtype definition of item to add.
46168      */
46169     addxtype : function(cfg) {
46170         return this.layout.addxtype(cfg);
46171     
46172     }
46173 });
46174
46175 Roo.ScrollPanel = function(el, config, content){
46176     config = config || {};
46177     config.fitToFrame = true;
46178     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46179     
46180     this.el.dom.style.overflow = "hidden";
46181     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46182     this.el.removeClass("x-layout-inactive-content");
46183     this.el.on("mousewheel", this.onWheel, this);
46184
46185     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46186     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46187     up.unselectable(); down.unselectable();
46188     up.on("click", this.scrollUp, this);
46189     down.on("click", this.scrollDown, this);
46190     up.addClassOnOver("x-scroller-btn-over");
46191     down.addClassOnOver("x-scroller-btn-over");
46192     up.addClassOnClick("x-scroller-btn-click");
46193     down.addClassOnClick("x-scroller-btn-click");
46194     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46195
46196     this.resizeEl = this.el;
46197     this.el = wrap; this.up = up; this.down = down;
46198 };
46199
46200 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46201     increment : 100,
46202     wheelIncrement : 5,
46203     scrollUp : function(){
46204         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46205     },
46206
46207     scrollDown : function(){
46208         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46209     },
46210
46211     afterScroll : function(){
46212         var el = this.resizeEl;
46213         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46214         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46215         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46216     },
46217
46218     setSize : function(){
46219         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46220         this.afterScroll();
46221     },
46222
46223     onWheel : function(e){
46224         var d = e.getWheelDelta();
46225         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46226         this.afterScroll();
46227         e.stopEvent();
46228     },
46229
46230     setContent : function(content, loadScripts){
46231         this.resizeEl.update(content, loadScripts);
46232     }
46233
46234 });
46235
46236
46237
46238
46239
46240
46241
46242
46243
46244 /**
46245  * @class Roo.TreePanel
46246  * @extends Roo.ContentPanel
46247  * @constructor
46248  * Create a new TreePanel. - defaults to fit/scoll contents.
46249  * @param {String/Object} config A string to set only the panel's title, or a config object
46250  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46251  */
46252 Roo.TreePanel = function(config){
46253     var el = config.el;
46254     var tree = config.tree;
46255     delete config.tree; 
46256     delete config.el; // hopefull!
46257     
46258     // wrapper for IE7 strict & safari scroll issue
46259     
46260     var treeEl = el.createChild();
46261     config.resizeEl = treeEl;
46262     
46263     
46264     
46265     Roo.TreePanel.superclass.constructor.call(this, el, config);
46266  
46267  
46268     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46269     //console.log(tree);
46270     this.on('activate', function()
46271     {
46272         if (this.tree.rendered) {
46273             return;
46274         }
46275         //console.log('render tree');
46276         this.tree.render();
46277     });
46278     
46279     this.on('resize',  function (cp, w, h) {
46280             this.tree.innerCt.setWidth(w);
46281             this.tree.innerCt.setHeight(h);
46282             this.tree.innerCt.setStyle('overflow-y', 'auto');
46283     });
46284
46285         
46286     
46287 };
46288
46289 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46290     fitToFrame : true,
46291     autoScroll : true
46292 });
46293
46294
46295
46296
46297
46298
46299
46300
46301
46302
46303
46304 /*
46305  * Based on:
46306  * Ext JS Library 1.1.1
46307  * Copyright(c) 2006-2007, Ext JS, LLC.
46308  *
46309  * Originally Released Under LGPL - original licence link has changed is not relivant.
46310  *
46311  * Fork - LGPL
46312  * <script type="text/javascript">
46313  */
46314  
46315
46316 /**
46317  * @class Roo.ReaderLayout
46318  * @extends Roo.BorderLayout
46319  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46320  * center region containing two nested regions (a top one for a list view and one for item preview below),
46321  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46322  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46323  * expedites the setup of the overall layout and regions for this common application style.
46324  * Example:
46325  <pre><code>
46326 var reader = new Roo.ReaderLayout();
46327 var CP = Roo.ContentPanel;  // shortcut for adding
46328
46329 reader.beginUpdate();
46330 reader.add("north", new CP("north", "North"));
46331 reader.add("west", new CP("west", {title: "West"}));
46332 reader.add("east", new CP("east", {title: "East"}));
46333
46334 reader.regions.listView.add(new CP("listView", "List"));
46335 reader.regions.preview.add(new CP("preview", "Preview"));
46336 reader.endUpdate();
46337 </code></pre>
46338 * @constructor
46339 * Create a new ReaderLayout
46340 * @param {Object} config Configuration options
46341 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46342 * document.body if omitted)
46343 */
46344 Roo.ReaderLayout = function(config, renderTo){
46345     var c = config || {size:{}};
46346     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46347         north: c.north !== false ? Roo.apply({
46348             split:false,
46349             initialSize: 32,
46350             titlebar: false
46351         }, c.north) : false,
46352         west: c.west !== false ? Roo.apply({
46353             split:true,
46354             initialSize: 200,
46355             minSize: 175,
46356             maxSize: 400,
46357             titlebar: true,
46358             collapsible: true,
46359             animate: true,
46360             margins:{left:5,right:0,bottom:5,top:5},
46361             cmargins:{left:5,right:5,bottom:5,top:5}
46362         }, c.west) : false,
46363         east: c.east !== false ? Roo.apply({
46364             split:true,
46365             initialSize: 200,
46366             minSize: 175,
46367             maxSize: 400,
46368             titlebar: true,
46369             collapsible: true,
46370             animate: true,
46371             margins:{left:0,right:5,bottom:5,top:5},
46372             cmargins:{left:5,right:5,bottom:5,top:5}
46373         }, c.east) : false,
46374         center: Roo.apply({
46375             tabPosition: 'top',
46376             autoScroll:false,
46377             closeOnTab: true,
46378             titlebar:false,
46379             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46380         }, c.center)
46381     });
46382
46383     this.el.addClass('x-reader');
46384
46385     this.beginUpdate();
46386
46387     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46388         south: c.preview !== false ? Roo.apply({
46389             split:true,
46390             initialSize: 200,
46391             minSize: 100,
46392             autoScroll:true,
46393             collapsible:true,
46394             titlebar: true,
46395             cmargins:{top:5,left:0, right:0, bottom:0}
46396         }, c.preview) : false,
46397         center: Roo.apply({
46398             autoScroll:false,
46399             titlebar:false,
46400             minHeight:200
46401         }, c.listView)
46402     });
46403     this.add('center', new Roo.NestedLayoutPanel(inner,
46404             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46405
46406     this.endUpdate();
46407
46408     this.regions.preview = inner.getRegion('south');
46409     this.regions.listView = inner.getRegion('center');
46410 };
46411
46412 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46413  * Based on:
46414  * Ext JS Library 1.1.1
46415  * Copyright(c) 2006-2007, Ext JS, LLC.
46416  *
46417  * Originally Released Under LGPL - original licence link has changed is not relivant.
46418  *
46419  * Fork - LGPL
46420  * <script type="text/javascript">
46421  */
46422  
46423 /**
46424  * @class Roo.grid.Grid
46425  * @extends Roo.util.Observable
46426  * This class represents the primary interface of a component based grid control.
46427  * <br><br>Usage:<pre><code>
46428  var grid = new Roo.grid.Grid("my-container-id", {
46429      ds: myDataStore,
46430      cm: myColModel,
46431      selModel: mySelectionModel,
46432      autoSizeColumns: true,
46433      monitorWindowResize: false,
46434      trackMouseOver: true
46435  });
46436  // set any options
46437  grid.render();
46438  * </code></pre>
46439  * <b>Common Problems:</b><br/>
46440  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46441  * element will correct this<br/>
46442  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46443  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46444  * are unpredictable.<br/>
46445  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46446  * grid to calculate dimensions/offsets.<br/>
46447   * @constructor
46448  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46449  * The container MUST have some type of size defined for the grid to fill. The container will be
46450  * automatically set to position relative if it isn't already.
46451  * @param {Object} config A config object that sets properties on this grid.
46452  */
46453 Roo.grid.Grid = function(container, config){
46454         // initialize the container
46455         this.container = Roo.get(container);
46456         this.container.update("");
46457         this.container.setStyle("overflow", "hidden");
46458     this.container.addClass('x-grid-container');
46459
46460     this.id = this.container.id;
46461
46462     Roo.apply(this, config);
46463     // check and correct shorthanded configs
46464     if(this.ds){
46465         this.dataSource = this.ds;
46466         delete this.ds;
46467     }
46468     if(this.cm){
46469         this.colModel = this.cm;
46470         delete this.cm;
46471     }
46472     if(this.sm){
46473         this.selModel = this.sm;
46474         delete this.sm;
46475     }
46476
46477     if (this.selModel) {
46478         this.selModel = Roo.factory(this.selModel, Roo.grid);
46479         this.sm = this.selModel;
46480         this.sm.xmodule = this.xmodule || false;
46481     }
46482     if (typeof(this.colModel.config) == 'undefined') {
46483         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46484         this.cm = this.colModel;
46485         this.cm.xmodule = this.xmodule || false;
46486     }
46487     if (this.dataSource) {
46488         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46489         this.ds = this.dataSource;
46490         this.ds.xmodule = this.xmodule || false;
46491          
46492     }
46493     
46494     
46495     
46496     if(this.width){
46497         this.container.setWidth(this.width);
46498     }
46499
46500     if(this.height){
46501         this.container.setHeight(this.height);
46502     }
46503     /** @private */
46504         this.addEvents({
46505         // raw events
46506         /**
46507          * @event click
46508          * The raw click event for the entire grid.
46509          * @param {Roo.EventObject} e
46510          */
46511         "click" : true,
46512         /**
46513          * @event dblclick
46514          * The raw dblclick event for the entire grid.
46515          * @param {Roo.EventObject} e
46516          */
46517         "dblclick" : true,
46518         /**
46519          * @event contextmenu
46520          * The raw contextmenu event for the entire grid.
46521          * @param {Roo.EventObject} e
46522          */
46523         "contextmenu" : true,
46524         /**
46525          * @event mousedown
46526          * The raw mousedown event for the entire grid.
46527          * @param {Roo.EventObject} e
46528          */
46529         "mousedown" : true,
46530         /**
46531          * @event mouseup
46532          * The raw mouseup event for the entire grid.
46533          * @param {Roo.EventObject} e
46534          */
46535         "mouseup" : true,
46536         /**
46537          * @event mouseover
46538          * The raw mouseover event for the entire grid.
46539          * @param {Roo.EventObject} e
46540          */
46541         "mouseover" : true,
46542         /**
46543          * @event mouseout
46544          * The raw mouseout event for the entire grid.
46545          * @param {Roo.EventObject} e
46546          */
46547         "mouseout" : true,
46548         /**
46549          * @event keypress
46550          * The raw keypress event for the entire grid.
46551          * @param {Roo.EventObject} e
46552          */
46553         "keypress" : true,
46554         /**
46555          * @event keydown
46556          * The raw keydown event for the entire grid.
46557          * @param {Roo.EventObject} e
46558          */
46559         "keydown" : true,
46560
46561         // custom events
46562
46563         /**
46564          * @event cellclick
46565          * Fires when a cell is clicked
46566          * @param {Grid} this
46567          * @param {Number} rowIndex
46568          * @param {Number} columnIndex
46569          * @param {Roo.EventObject} e
46570          */
46571         "cellclick" : true,
46572         /**
46573          * @event celldblclick
46574          * Fires when a cell is double clicked
46575          * @param {Grid} this
46576          * @param {Number} rowIndex
46577          * @param {Number} columnIndex
46578          * @param {Roo.EventObject} e
46579          */
46580         "celldblclick" : true,
46581         /**
46582          * @event rowclick
46583          * Fires when a row is clicked
46584          * @param {Grid} this
46585          * @param {Number} rowIndex
46586          * @param {Roo.EventObject} e
46587          */
46588         "rowclick" : true,
46589         /**
46590          * @event rowdblclick
46591          * Fires when a row is double clicked
46592          * @param {Grid} this
46593          * @param {Number} rowIndex
46594          * @param {Roo.EventObject} e
46595          */
46596         "rowdblclick" : true,
46597         /**
46598          * @event headerclick
46599          * Fires when a header is clicked
46600          * @param {Grid} this
46601          * @param {Number} columnIndex
46602          * @param {Roo.EventObject} e
46603          */
46604         "headerclick" : true,
46605         /**
46606          * @event headerdblclick
46607          * Fires when a header cell is double clicked
46608          * @param {Grid} this
46609          * @param {Number} columnIndex
46610          * @param {Roo.EventObject} e
46611          */
46612         "headerdblclick" : true,
46613         /**
46614          * @event rowcontextmenu
46615          * Fires when a row is right clicked
46616          * @param {Grid} this
46617          * @param {Number} rowIndex
46618          * @param {Roo.EventObject} e
46619          */
46620         "rowcontextmenu" : true,
46621         /**
46622          * @event cellcontextmenu
46623          * Fires when a cell is right clicked
46624          * @param {Grid} this
46625          * @param {Number} rowIndex
46626          * @param {Number} cellIndex
46627          * @param {Roo.EventObject} e
46628          */
46629          "cellcontextmenu" : true,
46630         /**
46631          * @event headercontextmenu
46632          * Fires when a header is right clicked
46633          * @param {Grid} this
46634          * @param {Number} columnIndex
46635          * @param {Roo.EventObject} e
46636          */
46637         "headercontextmenu" : true,
46638         /**
46639          * @event bodyscroll
46640          * Fires when the body element is scrolled
46641          * @param {Number} scrollLeft
46642          * @param {Number} scrollTop
46643          */
46644         "bodyscroll" : true,
46645         /**
46646          * @event columnresize
46647          * Fires when the user resizes a column
46648          * @param {Number} columnIndex
46649          * @param {Number} newSize
46650          */
46651         "columnresize" : true,
46652         /**
46653          * @event columnmove
46654          * Fires when the user moves a column
46655          * @param {Number} oldIndex
46656          * @param {Number} newIndex
46657          */
46658         "columnmove" : true,
46659         /**
46660          * @event startdrag
46661          * Fires when row(s) start being dragged
46662          * @param {Grid} this
46663          * @param {Roo.GridDD} dd The drag drop object
46664          * @param {event} e The raw browser event
46665          */
46666         "startdrag" : true,
46667         /**
46668          * @event enddrag
46669          * Fires when a drag operation is complete
46670          * @param {Grid} this
46671          * @param {Roo.GridDD} dd The drag drop object
46672          * @param {event} e The raw browser event
46673          */
46674         "enddrag" : true,
46675         /**
46676          * @event dragdrop
46677          * Fires when dragged row(s) are dropped on a valid DD target
46678          * @param {Grid} this
46679          * @param {Roo.GridDD} dd The drag drop object
46680          * @param {String} targetId The target drag drop object
46681          * @param {event} e The raw browser event
46682          */
46683         "dragdrop" : true,
46684         /**
46685          * @event dragover
46686          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46687          * @param {Grid} this
46688          * @param {Roo.GridDD} dd The drag drop object
46689          * @param {String} targetId The target drag drop object
46690          * @param {event} e The raw browser event
46691          */
46692         "dragover" : true,
46693         /**
46694          * @event dragenter
46695          *  Fires when the dragged row(s) first cross another DD target while being dragged
46696          * @param {Grid} this
46697          * @param {Roo.GridDD} dd The drag drop object
46698          * @param {String} targetId The target drag drop object
46699          * @param {event} e The raw browser event
46700          */
46701         "dragenter" : true,
46702         /**
46703          * @event dragout
46704          * Fires when the dragged row(s) leave another DD target while being dragged
46705          * @param {Grid} this
46706          * @param {Roo.GridDD} dd The drag drop object
46707          * @param {String} targetId The target drag drop object
46708          * @param {event} e The raw browser event
46709          */
46710         "dragout" : true,
46711         /**
46712          * @event rowclass
46713          * Fires when a row is rendered, so you can change add a style to it.
46714          * @param {GridView} gridview   The grid view
46715          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46716          */
46717         'rowclass' : true,
46718
46719         /**
46720          * @event render
46721          * Fires when the grid is rendered
46722          * @param {Grid} grid
46723          */
46724         'render' : true
46725     });
46726
46727     Roo.grid.Grid.superclass.constructor.call(this);
46728 };
46729 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46730     
46731     /**
46732      * @cfg {String} ddGroup - drag drop group.
46733      */
46734
46735     /**
46736      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46737      */
46738     minColumnWidth : 25,
46739
46740     /**
46741      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46742      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46743      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46744      */
46745     autoSizeColumns : false,
46746
46747     /**
46748      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46749      */
46750     autoSizeHeaders : true,
46751
46752     /**
46753      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46754      */
46755     monitorWindowResize : true,
46756
46757     /**
46758      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46759      * rows measured to get a columns size. Default is 0 (all rows).
46760      */
46761     maxRowsToMeasure : 0,
46762
46763     /**
46764      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46765      */
46766     trackMouseOver : true,
46767
46768     /**
46769     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46770     */
46771     
46772     /**
46773     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46774     */
46775     enableDragDrop : false,
46776     
46777     /**
46778     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46779     */
46780     enableColumnMove : true,
46781     
46782     /**
46783     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46784     */
46785     enableColumnHide : true,
46786     
46787     /**
46788     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46789     */
46790     enableRowHeightSync : false,
46791     
46792     /**
46793     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46794     */
46795     stripeRows : true,
46796     
46797     /**
46798     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46799     */
46800     autoHeight : false,
46801
46802     /**
46803      * @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.
46804      */
46805     autoExpandColumn : false,
46806
46807     /**
46808     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46809     * Default is 50.
46810     */
46811     autoExpandMin : 50,
46812
46813     /**
46814     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46815     */
46816     autoExpandMax : 1000,
46817
46818     /**
46819     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46820     */
46821     view : null,
46822
46823     /**
46824     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46825     */
46826     loadMask : false,
46827     /**
46828     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46829     */
46830     dropTarget: false,
46831     
46832    
46833     
46834     // private
46835     rendered : false,
46836
46837     /**
46838     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46839     * of a fixed width. Default is false.
46840     */
46841     /**
46842     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46843     */
46844     /**
46845      * Called once after all setup has been completed and the grid is ready to be rendered.
46846      * @return {Roo.grid.Grid} this
46847      */
46848     render : function()
46849     {
46850         var c = this.container;
46851         // try to detect autoHeight/width mode
46852         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46853             this.autoHeight = true;
46854         }
46855         var view = this.getView();
46856         view.init(this);
46857
46858         c.on("click", this.onClick, this);
46859         c.on("dblclick", this.onDblClick, this);
46860         c.on("contextmenu", this.onContextMenu, this);
46861         c.on("keydown", this.onKeyDown, this);
46862
46863         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46864
46865         this.getSelectionModel().init(this);
46866
46867         view.render();
46868
46869         if(this.loadMask){
46870             this.loadMask = new Roo.LoadMask(this.container,
46871                     Roo.apply({store:this.dataSource}, this.loadMask));
46872         }
46873         
46874         
46875         if (this.toolbar && this.toolbar.xtype) {
46876             this.toolbar.container = this.getView().getHeaderPanel(true);
46877             this.toolbar = new Roo.Toolbar(this.toolbar);
46878         }
46879         if (this.footer && this.footer.xtype) {
46880             this.footer.dataSource = this.getDataSource();
46881             this.footer.container = this.getView().getFooterPanel(true);
46882             this.footer = Roo.factory(this.footer, Roo);
46883         }
46884         if (this.dropTarget && this.dropTarget.xtype) {
46885             delete this.dropTarget.xtype;
46886             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46887         }
46888         
46889         
46890         this.rendered = true;
46891         this.fireEvent('render', this);
46892         return this;
46893     },
46894
46895         /**
46896          * Reconfigures the grid to use a different Store and Column Model.
46897          * The View will be bound to the new objects and refreshed.
46898          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46899          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46900          */
46901     reconfigure : function(dataSource, colModel){
46902         if(this.loadMask){
46903             this.loadMask.destroy();
46904             this.loadMask = new Roo.LoadMask(this.container,
46905                     Roo.apply({store:dataSource}, this.loadMask));
46906         }
46907         this.view.bind(dataSource, colModel);
46908         this.dataSource = dataSource;
46909         this.colModel = colModel;
46910         this.view.refresh(true);
46911     },
46912
46913     // private
46914     onKeyDown : function(e){
46915         this.fireEvent("keydown", e);
46916     },
46917
46918     /**
46919      * Destroy this grid.
46920      * @param {Boolean} removeEl True to remove the element
46921      */
46922     destroy : function(removeEl, keepListeners){
46923         if(this.loadMask){
46924             this.loadMask.destroy();
46925         }
46926         var c = this.container;
46927         c.removeAllListeners();
46928         this.view.destroy();
46929         this.colModel.purgeListeners();
46930         if(!keepListeners){
46931             this.purgeListeners();
46932         }
46933         c.update("");
46934         if(removeEl === true){
46935             c.remove();
46936         }
46937     },
46938
46939     // private
46940     processEvent : function(name, e){
46941         this.fireEvent(name, e);
46942         var t = e.getTarget();
46943         var v = this.view;
46944         var header = v.findHeaderIndex(t);
46945         if(header !== false){
46946             this.fireEvent("header" + name, this, header, e);
46947         }else{
46948             var row = v.findRowIndex(t);
46949             var cell = v.findCellIndex(t);
46950             if(row !== false){
46951                 this.fireEvent("row" + name, this, row, e);
46952                 if(cell !== false){
46953                     this.fireEvent("cell" + name, this, row, cell, e);
46954                 }
46955             }
46956         }
46957     },
46958
46959     // private
46960     onClick : function(e){
46961         this.processEvent("click", e);
46962     },
46963
46964     // private
46965     onContextMenu : function(e, t){
46966         this.processEvent("contextmenu", e);
46967     },
46968
46969     // private
46970     onDblClick : function(e){
46971         this.processEvent("dblclick", e);
46972     },
46973
46974     // private
46975     walkCells : function(row, col, step, fn, scope){
46976         var cm = this.colModel, clen = cm.getColumnCount();
46977         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46978         if(step < 0){
46979             if(col < 0){
46980                 row--;
46981                 first = false;
46982             }
46983             while(row >= 0){
46984                 if(!first){
46985                     col = clen-1;
46986                 }
46987                 first = false;
46988                 while(col >= 0){
46989                     if(fn.call(scope || this, row, col, cm) === true){
46990                         return [row, col];
46991                     }
46992                     col--;
46993                 }
46994                 row--;
46995             }
46996         } else {
46997             if(col >= clen){
46998                 row++;
46999                 first = false;
47000             }
47001             while(row < rlen){
47002                 if(!first){
47003                     col = 0;
47004                 }
47005                 first = false;
47006                 while(col < clen){
47007                     if(fn.call(scope || this, row, col, cm) === true){
47008                         return [row, col];
47009                     }
47010                     col++;
47011                 }
47012                 row++;
47013             }
47014         }
47015         return null;
47016     },
47017
47018     // private
47019     getSelections : function(){
47020         return this.selModel.getSelections();
47021     },
47022
47023     /**
47024      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47025      * but if manual update is required this method will initiate it.
47026      */
47027     autoSize : function(){
47028         if(this.rendered){
47029             this.view.layout();
47030             if(this.view.adjustForScroll){
47031                 this.view.adjustForScroll();
47032             }
47033         }
47034     },
47035
47036     /**
47037      * Returns the grid's underlying element.
47038      * @return {Element} The element
47039      */
47040     getGridEl : function(){
47041         return this.container;
47042     },
47043
47044     // private for compatibility, overridden by editor grid
47045     stopEditing : function(){},
47046
47047     /**
47048      * Returns the grid's SelectionModel.
47049      * @return {SelectionModel}
47050      */
47051     getSelectionModel : function(){
47052         if(!this.selModel){
47053             this.selModel = new Roo.grid.RowSelectionModel();
47054         }
47055         return this.selModel;
47056     },
47057
47058     /**
47059      * Returns the grid's DataSource.
47060      * @return {DataSource}
47061      */
47062     getDataSource : function(){
47063         return this.dataSource;
47064     },
47065
47066     /**
47067      * Returns the grid's ColumnModel.
47068      * @return {ColumnModel}
47069      */
47070     getColumnModel : function(){
47071         return this.colModel;
47072     },
47073
47074     /**
47075      * Returns the grid's GridView object.
47076      * @return {GridView}
47077      */
47078     getView : function(){
47079         if(!this.view){
47080             this.view = new Roo.grid.GridView(this.viewConfig);
47081         }
47082         return this.view;
47083     },
47084     /**
47085      * Called to get grid's drag proxy text, by default returns this.ddText.
47086      * @return {String}
47087      */
47088     getDragDropText : function(){
47089         var count = this.selModel.getCount();
47090         return String.format(this.ddText, count, count == 1 ? '' : 's');
47091     }
47092 });
47093 /**
47094  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47095  * %0 is replaced with the number of selected rows.
47096  * @type String
47097  */
47098 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47099  * Based on:
47100  * Ext JS Library 1.1.1
47101  * Copyright(c) 2006-2007, Ext JS, LLC.
47102  *
47103  * Originally Released Under LGPL - original licence link has changed is not relivant.
47104  *
47105  * Fork - LGPL
47106  * <script type="text/javascript">
47107  */
47108  
47109 Roo.grid.AbstractGridView = function(){
47110         this.grid = null;
47111         
47112         this.events = {
47113             "beforerowremoved" : true,
47114             "beforerowsinserted" : true,
47115             "beforerefresh" : true,
47116             "rowremoved" : true,
47117             "rowsinserted" : true,
47118             "rowupdated" : true,
47119             "refresh" : true
47120         };
47121     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47122 };
47123
47124 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47125     rowClass : "x-grid-row",
47126     cellClass : "x-grid-cell",
47127     tdClass : "x-grid-td",
47128     hdClass : "x-grid-hd",
47129     splitClass : "x-grid-hd-split",
47130     
47131         init: function(grid){
47132         this.grid = grid;
47133                 var cid = this.grid.getGridEl().id;
47134         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47135         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47136         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47137         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47138         },
47139         
47140         getColumnRenderers : function(){
47141         var renderers = [];
47142         var cm = this.grid.colModel;
47143         var colCount = cm.getColumnCount();
47144         for(var i = 0; i < colCount; i++){
47145             renderers[i] = cm.getRenderer(i);
47146         }
47147         return renderers;
47148     },
47149     
47150     getColumnIds : function(){
47151         var ids = [];
47152         var cm = this.grid.colModel;
47153         var colCount = cm.getColumnCount();
47154         for(var i = 0; i < colCount; i++){
47155             ids[i] = cm.getColumnId(i);
47156         }
47157         return ids;
47158     },
47159     
47160     getDataIndexes : function(){
47161         if(!this.indexMap){
47162             this.indexMap = this.buildIndexMap();
47163         }
47164         return this.indexMap.colToData;
47165     },
47166     
47167     getColumnIndexByDataIndex : function(dataIndex){
47168         if(!this.indexMap){
47169             this.indexMap = this.buildIndexMap();
47170         }
47171         return this.indexMap.dataToCol[dataIndex];
47172     },
47173     
47174     /**
47175      * Set a css style for a column dynamically. 
47176      * @param {Number} colIndex The index of the column
47177      * @param {String} name The css property name
47178      * @param {String} value The css value
47179      */
47180     setCSSStyle : function(colIndex, name, value){
47181         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47182         Roo.util.CSS.updateRule(selector, name, value);
47183     },
47184     
47185     generateRules : function(cm){
47186         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47187         Roo.util.CSS.removeStyleSheet(rulesId);
47188         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47189             var cid = cm.getColumnId(i);
47190             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47191                          this.tdSelector, cid, " {\n}\n",
47192                          this.hdSelector, cid, " {\n}\n",
47193                          this.splitSelector, cid, " {\n}\n");
47194         }
47195         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47196     }
47197 });/*
47198  * Based on:
47199  * Ext JS Library 1.1.1
47200  * Copyright(c) 2006-2007, Ext JS, LLC.
47201  *
47202  * Originally Released Under LGPL - original licence link has changed is not relivant.
47203  *
47204  * Fork - LGPL
47205  * <script type="text/javascript">
47206  */
47207
47208 // private
47209 // This is a support class used internally by the Grid components
47210 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47211     this.grid = grid;
47212     this.view = grid.getView();
47213     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47214     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47215     if(hd2){
47216         this.setHandleElId(Roo.id(hd));
47217         this.setOuterHandleElId(Roo.id(hd2));
47218     }
47219     this.scroll = false;
47220 };
47221 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47222     maxDragWidth: 120,
47223     getDragData : function(e){
47224         var t = Roo.lib.Event.getTarget(e);
47225         var h = this.view.findHeaderCell(t);
47226         if(h){
47227             return {ddel: h.firstChild, header:h};
47228         }
47229         return false;
47230     },
47231
47232     onInitDrag : function(e){
47233         this.view.headersDisabled = true;
47234         var clone = this.dragData.ddel.cloneNode(true);
47235         clone.id = Roo.id();
47236         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47237         this.proxy.update(clone);
47238         return true;
47239     },
47240
47241     afterValidDrop : function(){
47242         var v = this.view;
47243         setTimeout(function(){
47244             v.headersDisabled = false;
47245         }, 50);
47246     },
47247
47248     afterInvalidDrop : function(){
47249         var v = this.view;
47250         setTimeout(function(){
47251             v.headersDisabled = false;
47252         }, 50);
47253     }
47254 });
47255 /*
47256  * Based on:
47257  * Ext JS Library 1.1.1
47258  * Copyright(c) 2006-2007, Ext JS, LLC.
47259  *
47260  * Originally Released Under LGPL - original licence link has changed is not relivant.
47261  *
47262  * Fork - LGPL
47263  * <script type="text/javascript">
47264  */
47265 // private
47266 // This is a support class used internally by the Grid components
47267 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47268     this.grid = grid;
47269     this.view = grid.getView();
47270     // split the proxies so they don't interfere with mouse events
47271     this.proxyTop = Roo.DomHelper.append(document.body, {
47272         cls:"col-move-top", html:"&#160;"
47273     }, true);
47274     this.proxyBottom = Roo.DomHelper.append(document.body, {
47275         cls:"col-move-bottom", html:"&#160;"
47276     }, true);
47277     this.proxyTop.hide = this.proxyBottom.hide = function(){
47278         this.setLeftTop(-100,-100);
47279         this.setStyle("visibility", "hidden");
47280     };
47281     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47282     // temporarily disabled
47283     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47284     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47285 };
47286 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47287     proxyOffsets : [-4, -9],
47288     fly: Roo.Element.fly,
47289
47290     getTargetFromEvent : function(e){
47291         var t = Roo.lib.Event.getTarget(e);
47292         var cindex = this.view.findCellIndex(t);
47293         if(cindex !== false){
47294             return this.view.getHeaderCell(cindex);
47295         }
47296         return null;
47297     },
47298
47299     nextVisible : function(h){
47300         var v = this.view, cm = this.grid.colModel;
47301         h = h.nextSibling;
47302         while(h){
47303             if(!cm.isHidden(v.getCellIndex(h))){
47304                 return h;
47305             }
47306             h = h.nextSibling;
47307         }
47308         return null;
47309     },
47310
47311     prevVisible : function(h){
47312         var v = this.view, cm = this.grid.colModel;
47313         h = h.prevSibling;
47314         while(h){
47315             if(!cm.isHidden(v.getCellIndex(h))){
47316                 return h;
47317             }
47318             h = h.prevSibling;
47319         }
47320         return null;
47321     },
47322
47323     positionIndicator : function(h, n, e){
47324         var x = Roo.lib.Event.getPageX(e);
47325         var r = Roo.lib.Dom.getRegion(n.firstChild);
47326         var px, pt, py = r.top + this.proxyOffsets[1];
47327         if((r.right - x) <= (r.right-r.left)/2){
47328             px = r.right+this.view.borderWidth;
47329             pt = "after";
47330         }else{
47331             px = r.left;
47332             pt = "before";
47333         }
47334         var oldIndex = this.view.getCellIndex(h);
47335         var newIndex = this.view.getCellIndex(n);
47336
47337         if(this.grid.colModel.isFixed(newIndex)){
47338             return false;
47339         }
47340
47341         var locked = this.grid.colModel.isLocked(newIndex);
47342
47343         if(pt == "after"){
47344             newIndex++;
47345         }
47346         if(oldIndex < newIndex){
47347             newIndex--;
47348         }
47349         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47350             return false;
47351         }
47352         px +=  this.proxyOffsets[0];
47353         this.proxyTop.setLeftTop(px, py);
47354         this.proxyTop.show();
47355         if(!this.bottomOffset){
47356             this.bottomOffset = this.view.mainHd.getHeight();
47357         }
47358         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47359         this.proxyBottom.show();
47360         return pt;
47361     },
47362
47363     onNodeEnter : function(n, dd, e, data){
47364         if(data.header != n){
47365             this.positionIndicator(data.header, n, e);
47366         }
47367     },
47368
47369     onNodeOver : function(n, dd, e, data){
47370         var result = false;
47371         if(data.header != n){
47372             result = this.positionIndicator(data.header, n, e);
47373         }
47374         if(!result){
47375             this.proxyTop.hide();
47376             this.proxyBottom.hide();
47377         }
47378         return result ? this.dropAllowed : this.dropNotAllowed;
47379     },
47380
47381     onNodeOut : function(n, dd, e, data){
47382         this.proxyTop.hide();
47383         this.proxyBottom.hide();
47384     },
47385
47386     onNodeDrop : function(n, dd, e, data){
47387         var h = data.header;
47388         if(h != n){
47389             var cm = this.grid.colModel;
47390             var x = Roo.lib.Event.getPageX(e);
47391             var r = Roo.lib.Dom.getRegion(n.firstChild);
47392             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47393             var oldIndex = this.view.getCellIndex(h);
47394             var newIndex = this.view.getCellIndex(n);
47395             var locked = cm.isLocked(newIndex);
47396             if(pt == "after"){
47397                 newIndex++;
47398             }
47399             if(oldIndex < newIndex){
47400                 newIndex--;
47401             }
47402             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47403                 return false;
47404             }
47405             cm.setLocked(oldIndex, locked, true);
47406             cm.moveColumn(oldIndex, newIndex);
47407             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47408             return true;
47409         }
47410         return false;
47411     }
47412 });
47413 /*
47414  * Based on:
47415  * Ext JS Library 1.1.1
47416  * Copyright(c) 2006-2007, Ext JS, LLC.
47417  *
47418  * Originally Released Under LGPL - original licence link has changed is not relivant.
47419  *
47420  * Fork - LGPL
47421  * <script type="text/javascript">
47422  */
47423   
47424 /**
47425  * @class Roo.grid.GridView
47426  * @extends Roo.util.Observable
47427  *
47428  * @constructor
47429  * @param {Object} config
47430  */
47431 Roo.grid.GridView = function(config){
47432     Roo.grid.GridView.superclass.constructor.call(this);
47433     this.el = null;
47434
47435     Roo.apply(this, config);
47436 };
47437
47438 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47439
47440     /**
47441      * Override this function to apply custom css classes to rows during rendering
47442      * @param {Record} record The record
47443      * @param {Number} index
47444      * @method getRowClass
47445      */
47446     rowClass : "x-grid-row",
47447
47448     cellClass : "x-grid-col",
47449
47450     tdClass : "x-grid-td",
47451
47452     hdClass : "x-grid-hd",
47453
47454     splitClass : "x-grid-split",
47455
47456     sortClasses : ["sort-asc", "sort-desc"],
47457
47458     enableMoveAnim : false,
47459
47460     hlColor: "C3DAF9",
47461
47462     dh : Roo.DomHelper,
47463
47464     fly : Roo.Element.fly,
47465
47466     css : Roo.util.CSS,
47467
47468     borderWidth: 1,
47469
47470     splitOffset: 3,
47471
47472     scrollIncrement : 22,
47473
47474     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47475
47476     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47477
47478     bind : function(ds, cm){
47479         if(this.ds){
47480             this.ds.un("load", this.onLoad, this);
47481             this.ds.un("datachanged", this.onDataChange, this);
47482             this.ds.un("add", this.onAdd, this);
47483             this.ds.un("remove", this.onRemove, this);
47484             this.ds.un("update", this.onUpdate, this);
47485             this.ds.un("clear", this.onClear, this);
47486         }
47487         if(ds){
47488             ds.on("load", this.onLoad, this);
47489             ds.on("datachanged", this.onDataChange, this);
47490             ds.on("add", this.onAdd, this);
47491             ds.on("remove", this.onRemove, this);
47492             ds.on("update", this.onUpdate, this);
47493             ds.on("clear", this.onClear, this);
47494         }
47495         this.ds = ds;
47496
47497         if(this.cm){
47498             this.cm.un("widthchange", this.onColWidthChange, this);
47499             this.cm.un("headerchange", this.onHeaderChange, this);
47500             this.cm.un("hiddenchange", this.onHiddenChange, this);
47501             this.cm.un("columnmoved", this.onColumnMove, this);
47502             this.cm.un("columnlockchange", this.onColumnLock, this);
47503         }
47504         if(cm){
47505             this.generateRules(cm);
47506             cm.on("widthchange", this.onColWidthChange, this);
47507             cm.on("headerchange", this.onHeaderChange, this);
47508             cm.on("hiddenchange", this.onHiddenChange, this);
47509             cm.on("columnmoved", this.onColumnMove, this);
47510             cm.on("columnlockchange", this.onColumnLock, this);
47511         }
47512         this.cm = cm;
47513     },
47514
47515     init: function(grid){
47516         Roo.grid.GridView.superclass.init.call(this, grid);
47517
47518         this.bind(grid.dataSource, grid.colModel);
47519
47520         grid.on("headerclick", this.handleHeaderClick, this);
47521
47522         if(grid.trackMouseOver){
47523             grid.on("mouseover", this.onRowOver, this);
47524             grid.on("mouseout", this.onRowOut, this);
47525         }
47526         grid.cancelTextSelection = function(){};
47527         this.gridId = grid.id;
47528
47529         var tpls = this.templates || {};
47530
47531         if(!tpls.master){
47532             tpls.master = new Roo.Template(
47533                '<div class="x-grid" hidefocus="true">',
47534                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47535                   '<div class="x-grid-topbar"></div>',
47536                   '<div class="x-grid-scroller"><div></div></div>',
47537                   '<div class="x-grid-locked">',
47538                       '<div class="x-grid-header">{lockedHeader}</div>',
47539                       '<div class="x-grid-body">{lockedBody}</div>',
47540                   "</div>",
47541                   '<div class="x-grid-viewport">',
47542                       '<div class="x-grid-header">{header}</div>',
47543                       '<div class="x-grid-body">{body}</div>',
47544                   "</div>",
47545                   '<div class="x-grid-bottombar"></div>',
47546                  
47547                   '<div class="x-grid-resize-proxy">&#160;</div>',
47548                "</div>"
47549             );
47550             tpls.master.disableformats = true;
47551         }
47552
47553         if(!tpls.header){
47554             tpls.header = new Roo.Template(
47555                '<table border="0" cellspacing="0" cellpadding="0">',
47556                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47557                "</table>{splits}"
47558             );
47559             tpls.header.disableformats = true;
47560         }
47561         tpls.header.compile();
47562
47563         if(!tpls.hcell){
47564             tpls.hcell = new Roo.Template(
47565                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47566                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47567                 "</div></td>"
47568              );
47569              tpls.hcell.disableFormats = true;
47570         }
47571         tpls.hcell.compile();
47572
47573         if(!tpls.hsplit){
47574             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47575             tpls.hsplit.disableFormats = true;
47576         }
47577         tpls.hsplit.compile();
47578
47579         if(!tpls.body){
47580             tpls.body = new Roo.Template(
47581                '<table border="0" cellspacing="0" cellpadding="0">',
47582                "<tbody>{rows}</tbody>",
47583                "</table>"
47584             );
47585             tpls.body.disableFormats = true;
47586         }
47587         tpls.body.compile();
47588
47589         if(!tpls.row){
47590             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47591             tpls.row.disableFormats = true;
47592         }
47593         tpls.row.compile();
47594
47595         if(!tpls.cell){
47596             tpls.cell = new Roo.Template(
47597                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47598                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47599                 "</td>"
47600             );
47601             tpls.cell.disableFormats = true;
47602         }
47603         tpls.cell.compile();
47604
47605         this.templates = tpls;
47606     },
47607
47608     // remap these for backwards compat
47609     onColWidthChange : function(){
47610         this.updateColumns.apply(this, arguments);
47611     },
47612     onHeaderChange : function(){
47613         this.updateHeaders.apply(this, arguments);
47614     }, 
47615     onHiddenChange : function(){
47616         this.handleHiddenChange.apply(this, arguments);
47617     },
47618     onColumnMove : function(){
47619         this.handleColumnMove.apply(this, arguments);
47620     },
47621     onColumnLock : function(){
47622         this.handleLockChange.apply(this, arguments);
47623     },
47624
47625     onDataChange : function(){
47626         this.refresh();
47627         this.updateHeaderSortState();
47628     },
47629
47630     onClear : function(){
47631         this.refresh();
47632     },
47633
47634     onUpdate : function(ds, record){
47635         this.refreshRow(record);
47636     },
47637
47638     refreshRow : function(record){
47639         var ds = this.ds, index;
47640         if(typeof record == 'number'){
47641             index = record;
47642             record = ds.getAt(index);
47643         }else{
47644             index = ds.indexOf(record);
47645         }
47646         this.insertRows(ds, index, index, true);
47647         this.onRemove(ds, record, index+1, true);
47648         this.syncRowHeights(index, index);
47649         this.layout();
47650         this.fireEvent("rowupdated", this, index, record);
47651     },
47652
47653     onAdd : function(ds, records, index){
47654         this.insertRows(ds, index, index + (records.length-1));
47655     },
47656
47657     onRemove : function(ds, record, index, isUpdate){
47658         if(isUpdate !== true){
47659             this.fireEvent("beforerowremoved", this, index, record);
47660         }
47661         var bt = this.getBodyTable(), lt = this.getLockedTable();
47662         if(bt.rows[index]){
47663             bt.firstChild.removeChild(bt.rows[index]);
47664         }
47665         if(lt.rows[index]){
47666             lt.firstChild.removeChild(lt.rows[index]);
47667         }
47668         if(isUpdate !== true){
47669             this.stripeRows(index);
47670             this.syncRowHeights(index, index);
47671             this.layout();
47672             this.fireEvent("rowremoved", this, index, record);
47673         }
47674     },
47675
47676     onLoad : function(){
47677         this.scrollToTop();
47678     },
47679
47680     /**
47681      * Scrolls the grid to the top
47682      */
47683     scrollToTop : function(){
47684         if(this.scroller){
47685             this.scroller.dom.scrollTop = 0;
47686             this.syncScroll();
47687         }
47688     },
47689
47690     /**
47691      * Gets a panel in the header of the grid that can be used for toolbars etc.
47692      * After modifying the contents of this panel a call to grid.autoSize() may be
47693      * required to register any changes in size.
47694      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47695      * @return Roo.Element
47696      */
47697     getHeaderPanel : function(doShow){
47698         if(doShow){
47699             this.headerPanel.show();
47700         }
47701         return this.headerPanel;
47702     },
47703
47704     /**
47705      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47706      * After modifying the contents of this panel a call to grid.autoSize() may be
47707      * required to register any changes in size.
47708      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47709      * @return Roo.Element
47710      */
47711     getFooterPanel : function(doShow){
47712         if(doShow){
47713             this.footerPanel.show();
47714         }
47715         return this.footerPanel;
47716     },
47717
47718     initElements : function(){
47719         var E = Roo.Element;
47720         var el = this.grid.getGridEl().dom.firstChild;
47721         var cs = el.childNodes;
47722
47723         this.el = new E(el);
47724         
47725          this.focusEl = new E(el.firstChild);
47726         this.focusEl.swallowEvent("click", true);
47727         
47728         this.headerPanel = new E(cs[1]);
47729         this.headerPanel.enableDisplayMode("block");
47730
47731         this.scroller = new E(cs[2]);
47732         this.scrollSizer = new E(this.scroller.dom.firstChild);
47733
47734         this.lockedWrap = new E(cs[3]);
47735         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47736         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47737
47738         this.mainWrap = new E(cs[4]);
47739         this.mainHd = new E(this.mainWrap.dom.firstChild);
47740         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47741
47742         this.footerPanel = new E(cs[5]);
47743         this.footerPanel.enableDisplayMode("block");
47744
47745         this.resizeProxy = new E(cs[6]);
47746
47747         this.headerSelector = String.format(
47748            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47749            this.lockedHd.id, this.mainHd.id
47750         );
47751
47752         this.splitterSelector = String.format(
47753            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47754            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47755         );
47756     },
47757     idToCssName : function(s)
47758     {
47759         return s.replace(/[^a-z0-9]+/ig, '-');
47760     },
47761
47762     getHeaderCell : function(index){
47763         return Roo.DomQuery.select(this.headerSelector)[index];
47764     },
47765
47766     getHeaderCellMeasure : function(index){
47767         return this.getHeaderCell(index).firstChild;
47768     },
47769
47770     getHeaderCellText : function(index){
47771         return this.getHeaderCell(index).firstChild.firstChild;
47772     },
47773
47774     getLockedTable : function(){
47775         return this.lockedBody.dom.firstChild;
47776     },
47777
47778     getBodyTable : function(){
47779         return this.mainBody.dom.firstChild;
47780     },
47781
47782     getLockedRow : function(index){
47783         return this.getLockedTable().rows[index];
47784     },
47785
47786     getRow : function(index){
47787         return this.getBodyTable().rows[index];
47788     },
47789
47790     getRowComposite : function(index){
47791         if(!this.rowEl){
47792             this.rowEl = new Roo.CompositeElementLite();
47793         }
47794         var els = [], lrow, mrow;
47795         if(lrow = this.getLockedRow(index)){
47796             els.push(lrow);
47797         }
47798         if(mrow = this.getRow(index)){
47799             els.push(mrow);
47800         }
47801         this.rowEl.elements = els;
47802         return this.rowEl;
47803     },
47804
47805     getCell : function(rowIndex, colIndex){
47806         var locked = this.cm.getLockedCount();
47807         var source;
47808         if(colIndex < locked){
47809             source = this.lockedBody.dom.firstChild;
47810         }else{
47811             source = this.mainBody.dom.firstChild;
47812             colIndex -= locked;
47813         }
47814         return source.rows[rowIndex].childNodes[colIndex];
47815     },
47816
47817     getCellText : function(rowIndex, colIndex){
47818         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47819     },
47820
47821     getCellBox : function(cell){
47822         var b = this.fly(cell).getBox();
47823         if(Roo.isOpera){ // opera fails to report the Y
47824             b.y = cell.offsetTop + this.mainBody.getY();
47825         }
47826         return b;
47827     },
47828
47829     getCellIndex : function(cell){
47830         var id = String(cell.className).match(this.cellRE);
47831         if(id){
47832             return parseInt(id[1], 10);
47833         }
47834         return 0;
47835     },
47836
47837     findHeaderIndex : function(n){
47838         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47839         return r ? this.getCellIndex(r) : false;
47840     },
47841
47842     findHeaderCell : function(n){
47843         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47844         return r ? r : false;
47845     },
47846
47847     findRowIndex : function(n){
47848         if(!n){
47849             return false;
47850         }
47851         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47852         return r ? r.rowIndex : false;
47853     },
47854
47855     findCellIndex : function(node){
47856         var stop = this.el.dom;
47857         while(node && node != stop){
47858             if(this.findRE.test(node.className)){
47859                 return this.getCellIndex(node);
47860             }
47861             node = node.parentNode;
47862         }
47863         return false;
47864     },
47865
47866     getColumnId : function(index){
47867         return this.cm.getColumnId(index);
47868     },
47869
47870     getSplitters : function()
47871     {
47872         if(this.splitterSelector){
47873            return Roo.DomQuery.select(this.splitterSelector);
47874         }else{
47875             return null;
47876       }
47877     },
47878
47879     getSplitter : function(index){
47880         return this.getSplitters()[index];
47881     },
47882
47883     onRowOver : function(e, t){
47884         var row;
47885         if((row = this.findRowIndex(t)) !== false){
47886             this.getRowComposite(row).addClass("x-grid-row-over");
47887         }
47888     },
47889
47890     onRowOut : function(e, t){
47891         var row;
47892         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47893             this.getRowComposite(row).removeClass("x-grid-row-over");
47894         }
47895     },
47896
47897     renderHeaders : function(){
47898         var cm = this.cm;
47899         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47900         var cb = [], lb = [], sb = [], lsb = [], p = {};
47901         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47902             p.cellId = "x-grid-hd-0-" + i;
47903             p.splitId = "x-grid-csplit-0-" + i;
47904             p.id = cm.getColumnId(i);
47905             p.title = cm.getColumnTooltip(i) || "";
47906             p.value = cm.getColumnHeader(i) || "";
47907             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47908             if(!cm.isLocked(i)){
47909                 cb[cb.length] = ct.apply(p);
47910                 sb[sb.length] = st.apply(p);
47911             }else{
47912                 lb[lb.length] = ct.apply(p);
47913                 lsb[lsb.length] = st.apply(p);
47914             }
47915         }
47916         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47917                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47918     },
47919
47920     updateHeaders : function(){
47921         var html = this.renderHeaders();
47922         this.lockedHd.update(html[0]);
47923         this.mainHd.update(html[1]);
47924     },
47925
47926     /**
47927      * Focuses the specified row.
47928      * @param {Number} row The row index
47929      */
47930     focusRow : function(row)
47931     {
47932         //Roo.log('GridView.focusRow');
47933         var x = this.scroller.dom.scrollLeft;
47934         this.focusCell(row, 0, false);
47935         this.scroller.dom.scrollLeft = x;
47936     },
47937
47938     /**
47939      * Focuses the specified cell.
47940      * @param {Number} row The row index
47941      * @param {Number} col The column index
47942      * @param {Boolean} hscroll false to disable horizontal scrolling
47943      */
47944     focusCell : function(row, col, hscroll)
47945     {
47946         //Roo.log('GridView.focusCell');
47947         var el = this.ensureVisible(row, col, hscroll);
47948         this.focusEl.alignTo(el, "tl-tl");
47949         if(Roo.isGecko){
47950             this.focusEl.focus();
47951         }else{
47952             this.focusEl.focus.defer(1, this.focusEl);
47953         }
47954     },
47955
47956     /**
47957      * Scrolls the specified cell into view
47958      * @param {Number} row The row index
47959      * @param {Number} col The column index
47960      * @param {Boolean} hscroll false to disable horizontal scrolling
47961      */
47962     ensureVisible : function(row, col, hscroll)
47963     {
47964         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47965         //return null; //disable for testing.
47966         if(typeof row != "number"){
47967             row = row.rowIndex;
47968         }
47969         if(row < 0 && row >= this.ds.getCount()){
47970             return  null;
47971         }
47972         col = (col !== undefined ? col : 0);
47973         var cm = this.grid.colModel;
47974         while(cm.isHidden(col)){
47975             col++;
47976         }
47977
47978         var el = this.getCell(row, col);
47979         if(!el){
47980             return null;
47981         }
47982         var c = this.scroller.dom;
47983
47984         var ctop = parseInt(el.offsetTop, 10);
47985         var cleft = parseInt(el.offsetLeft, 10);
47986         var cbot = ctop + el.offsetHeight;
47987         var cright = cleft + el.offsetWidth;
47988         
47989         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47990         var stop = parseInt(c.scrollTop, 10);
47991         var sleft = parseInt(c.scrollLeft, 10);
47992         var sbot = stop + ch;
47993         var sright = sleft + c.clientWidth;
47994         /*
47995         Roo.log('GridView.ensureVisible:' +
47996                 ' ctop:' + ctop +
47997                 ' c.clientHeight:' + c.clientHeight +
47998                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
47999                 ' stop:' + stop +
48000                 ' cbot:' + cbot +
48001                 ' sbot:' + sbot +
48002                 ' ch:' + ch  
48003                 );
48004         */
48005         if(ctop < stop){
48006              c.scrollTop = ctop;
48007             //Roo.log("set scrolltop to ctop DISABLE?");
48008         }else if(cbot > sbot){
48009             //Roo.log("set scrolltop to cbot-ch");
48010             c.scrollTop = cbot-ch;
48011         }
48012         
48013         if(hscroll !== false){
48014             if(cleft < sleft){
48015                 c.scrollLeft = cleft;
48016             }else if(cright > sright){
48017                 c.scrollLeft = cright-c.clientWidth;
48018             }
48019         }
48020          
48021         return el;
48022     },
48023
48024     updateColumns : function(){
48025         this.grid.stopEditing();
48026         var cm = this.grid.colModel, colIds = this.getColumnIds();
48027         //var totalWidth = cm.getTotalWidth();
48028         var pos = 0;
48029         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48030             //if(cm.isHidden(i)) continue;
48031             var w = cm.getColumnWidth(i);
48032             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48033             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48034         }
48035         this.updateSplitters();
48036     },
48037
48038     generateRules : function(cm){
48039         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48040         Roo.util.CSS.removeStyleSheet(rulesId);
48041         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48042             var cid = cm.getColumnId(i);
48043             var align = '';
48044             if(cm.config[i].align){
48045                 align = 'text-align:'+cm.config[i].align+';';
48046             }
48047             var hidden = '';
48048             if(cm.isHidden(i)){
48049                 hidden = 'display:none;';
48050             }
48051             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48052             ruleBuf.push(
48053                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48054                     this.hdSelector, cid, " {\n", align, width, "}\n",
48055                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48056                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48057         }
48058         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48059     },
48060
48061     updateSplitters : function(){
48062         var cm = this.cm, s = this.getSplitters();
48063         if(s){ // splitters not created yet
48064             var pos = 0, locked = true;
48065             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48066                 if(cm.isHidden(i)) continue;
48067                 var w = cm.getColumnWidth(i); // make sure it's a number
48068                 if(!cm.isLocked(i) && locked){
48069                     pos = 0;
48070                     locked = false;
48071                 }
48072                 pos += w;
48073                 s[i].style.left = (pos-this.splitOffset) + "px";
48074             }
48075         }
48076     },
48077
48078     handleHiddenChange : function(colModel, colIndex, hidden){
48079         if(hidden){
48080             this.hideColumn(colIndex);
48081         }else{
48082             this.unhideColumn(colIndex);
48083         }
48084     },
48085
48086     hideColumn : function(colIndex){
48087         var cid = this.getColumnId(colIndex);
48088         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48089         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48090         if(Roo.isSafari){
48091             this.updateHeaders();
48092         }
48093         this.updateSplitters();
48094         this.layout();
48095     },
48096
48097     unhideColumn : function(colIndex){
48098         var cid = this.getColumnId(colIndex);
48099         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48100         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48101
48102         if(Roo.isSafari){
48103             this.updateHeaders();
48104         }
48105         this.updateSplitters();
48106         this.layout();
48107     },
48108
48109     insertRows : function(dm, firstRow, lastRow, isUpdate){
48110         if(firstRow == 0 && lastRow == dm.getCount()-1){
48111             this.refresh();
48112         }else{
48113             if(!isUpdate){
48114                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48115             }
48116             var s = this.getScrollState();
48117             var markup = this.renderRows(firstRow, lastRow);
48118             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48119             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48120             this.restoreScroll(s);
48121             if(!isUpdate){
48122                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48123                 this.syncRowHeights(firstRow, lastRow);
48124                 this.stripeRows(firstRow);
48125                 this.layout();
48126             }
48127         }
48128     },
48129
48130     bufferRows : function(markup, target, index){
48131         var before = null, trows = target.rows, tbody = target.tBodies[0];
48132         if(index < trows.length){
48133             before = trows[index];
48134         }
48135         var b = document.createElement("div");
48136         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48137         var rows = b.firstChild.rows;
48138         for(var i = 0, len = rows.length; i < len; i++){
48139             if(before){
48140                 tbody.insertBefore(rows[0], before);
48141             }else{
48142                 tbody.appendChild(rows[0]);
48143             }
48144         }
48145         b.innerHTML = "";
48146         b = null;
48147     },
48148
48149     deleteRows : function(dm, firstRow, lastRow){
48150         if(dm.getRowCount()<1){
48151             this.fireEvent("beforerefresh", this);
48152             this.mainBody.update("");
48153             this.lockedBody.update("");
48154             this.fireEvent("refresh", this);
48155         }else{
48156             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48157             var bt = this.getBodyTable();
48158             var tbody = bt.firstChild;
48159             var rows = bt.rows;
48160             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48161                 tbody.removeChild(rows[firstRow]);
48162             }
48163             this.stripeRows(firstRow);
48164             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48165         }
48166     },
48167
48168     updateRows : function(dataSource, firstRow, lastRow){
48169         var s = this.getScrollState();
48170         this.refresh();
48171         this.restoreScroll(s);
48172     },
48173
48174     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48175         if(!noRefresh){
48176            this.refresh();
48177         }
48178         this.updateHeaderSortState();
48179     },
48180
48181     getScrollState : function(){
48182         
48183         var sb = this.scroller.dom;
48184         return {left: sb.scrollLeft, top: sb.scrollTop};
48185     },
48186
48187     stripeRows : function(startRow){
48188         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48189             return;
48190         }
48191         startRow = startRow || 0;
48192         var rows = this.getBodyTable().rows;
48193         var lrows = this.getLockedTable().rows;
48194         var cls = ' x-grid-row-alt ';
48195         for(var i = startRow, len = rows.length; i < len; i++){
48196             var row = rows[i], lrow = lrows[i];
48197             var isAlt = ((i+1) % 2 == 0);
48198             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48199             if(isAlt == hasAlt){
48200                 continue;
48201             }
48202             if(isAlt){
48203                 row.className += " x-grid-row-alt";
48204             }else{
48205                 row.className = row.className.replace("x-grid-row-alt", "");
48206             }
48207             if(lrow){
48208                 lrow.className = row.className;
48209             }
48210         }
48211     },
48212
48213     restoreScroll : function(state){
48214         //Roo.log('GridView.restoreScroll');
48215         var sb = this.scroller.dom;
48216         sb.scrollLeft = state.left;
48217         sb.scrollTop = state.top;
48218         this.syncScroll();
48219     },
48220
48221     syncScroll : function(){
48222         //Roo.log('GridView.syncScroll');
48223         var sb = this.scroller.dom;
48224         var sh = this.mainHd.dom;
48225         var bs = this.mainBody.dom;
48226         var lv = this.lockedBody.dom;
48227         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48228         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48229     },
48230
48231     handleScroll : function(e){
48232         this.syncScroll();
48233         var sb = this.scroller.dom;
48234         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48235         e.stopEvent();
48236     },
48237
48238     handleWheel : function(e){
48239         var d = e.getWheelDelta();
48240         this.scroller.dom.scrollTop -= d*22;
48241         // set this here to prevent jumpy scrolling on large tables
48242         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48243         e.stopEvent();
48244     },
48245
48246     renderRows : function(startRow, endRow){
48247         // pull in all the crap needed to render rows
48248         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48249         var colCount = cm.getColumnCount();
48250
48251         if(ds.getCount() < 1){
48252             return ["", ""];
48253         }
48254
48255         // build a map for all the columns
48256         var cs = [];
48257         for(var i = 0; i < colCount; i++){
48258             var name = cm.getDataIndex(i);
48259             cs[i] = {
48260                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48261                 renderer : cm.getRenderer(i),
48262                 id : cm.getColumnId(i),
48263                 locked : cm.isLocked(i)
48264             };
48265         }
48266
48267         startRow = startRow || 0;
48268         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48269
48270         // records to render
48271         var rs = ds.getRange(startRow, endRow);
48272
48273         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48274     },
48275
48276     // As much as I hate to duplicate code, this was branched because FireFox really hates
48277     // [].join("") on strings. The performance difference was substantial enough to
48278     // branch this function
48279     doRender : Roo.isGecko ?
48280             function(cs, rs, ds, startRow, colCount, stripe){
48281                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48282                 // buffers
48283                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48284                 
48285                 var hasListener = this.grid.hasListener('rowclass');
48286                 var rowcfg = {};
48287                 for(var j = 0, len = rs.length; j < len; j++){
48288                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48289                     for(var i = 0; i < colCount; i++){
48290                         c = cs[i];
48291                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48292                         p.id = c.id;
48293                         p.css = p.attr = "";
48294                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48295                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48296                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48297                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48298                         }
48299                         var markup = ct.apply(p);
48300                         if(!c.locked){
48301                             cb+= markup;
48302                         }else{
48303                             lcb+= markup;
48304                         }
48305                     }
48306                     var alt = [];
48307                     if(stripe && ((rowIndex+1) % 2 == 0)){
48308                         alt.push("x-grid-row-alt")
48309                     }
48310                     if(r.dirty){
48311                         alt.push(  " x-grid-dirty-row");
48312                     }
48313                     rp.cells = lcb;
48314                     if(this.getRowClass){
48315                         alt.push(this.getRowClass(r, rowIndex));
48316                     }
48317                     if (hasListener) {
48318                         rowcfg = {
48319                              
48320                             record: r,
48321                             rowIndex : rowIndex,
48322                             rowClass : ''
48323                         }
48324                         this.grid.fireEvent('rowclass', this, rowcfg);
48325                         alt.push(rowcfg.rowClass);
48326                     }
48327                     rp.alt = alt.join(" ");
48328                     lbuf+= rt.apply(rp);
48329                     rp.cells = cb;
48330                     buf+=  rt.apply(rp);
48331                 }
48332                 return [lbuf, buf];
48333             } :
48334             function(cs, rs, ds, startRow, colCount, stripe){
48335                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48336                 // buffers
48337                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48338                 var hasListener = this.grid.hasListener('rowclass');
48339                 var rowcfg = {};
48340                 for(var j = 0, len = rs.length; j < len; j++){
48341                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48342                     for(var i = 0; i < colCount; i++){
48343                         c = cs[i];
48344                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48345                         p.id = c.id;
48346                         p.css = p.attr = "";
48347                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48348                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48349                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48350                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48351                         }
48352                         var markup = ct.apply(p);
48353                         if(!c.locked){
48354                             cb[cb.length] = markup;
48355                         }else{
48356                             lcb[lcb.length] = markup;
48357                         }
48358                     }
48359                     var alt = [];
48360                     if(stripe && ((rowIndex+1) % 2 == 0)){
48361                         alt.push( "x-grid-row-alt");
48362                     }
48363                     if(r.dirty){
48364                         alt.push(" x-grid-dirty-row");
48365                     }
48366                     rp.cells = lcb;
48367                     if(this.getRowClass){
48368                         alt.push( this.getRowClass(r, rowIndex));
48369                     }
48370                     if (hasListener) {
48371                         rowcfg = {
48372                              
48373                             record: r,
48374                             rowIndex : rowIndex,
48375                             rowClass : ''
48376                         }
48377                         this.grid.fireEvent('rowclass', this, rowcfg);
48378                         alt.push(rowcfg.rowClass);
48379                     }
48380                     rp.alt = alt.join(" ");
48381                     rp.cells = lcb.join("");
48382                     lbuf[lbuf.length] = rt.apply(rp);
48383                     rp.cells = cb.join("");
48384                     buf[buf.length] =  rt.apply(rp);
48385                 }
48386                 return [lbuf.join(""), buf.join("")];
48387             },
48388
48389     renderBody : function(){
48390         var markup = this.renderRows();
48391         var bt = this.templates.body;
48392         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48393     },
48394
48395     /**
48396      * Refreshes the grid
48397      * @param {Boolean} headersToo
48398      */
48399     refresh : function(headersToo){
48400         this.fireEvent("beforerefresh", this);
48401         this.grid.stopEditing();
48402         var result = this.renderBody();
48403         this.lockedBody.update(result[0]);
48404         this.mainBody.update(result[1]);
48405         if(headersToo === true){
48406             this.updateHeaders();
48407             this.updateColumns();
48408             this.updateSplitters();
48409             this.updateHeaderSortState();
48410         }
48411         this.syncRowHeights();
48412         this.layout();
48413         this.fireEvent("refresh", this);
48414     },
48415
48416     handleColumnMove : function(cm, oldIndex, newIndex){
48417         this.indexMap = null;
48418         var s = this.getScrollState();
48419         this.refresh(true);
48420         this.restoreScroll(s);
48421         this.afterMove(newIndex);
48422     },
48423
48424     afterMove : function(colIndex){
48425         if(this.enableMoveAnim && Roo.enableFx){
48426             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48427         }
48428         // if multisort - fix sortOrder, and reload..
48429         if (this.grid.dataSource.multiSort) {
48430             // the we can call sort again..
48431             var dm = this.grid.dataSource;
48432             var cm = this.grid.colModel;
48433             var so = [];
48434             for(var i = 0; i < cm.config.length; i++ ) {
48435                 
48436                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48437                     continue; // dont' bother, it's not in sort list or being set.
48438                 }
48439                 
48440                 so.push(cm.config[i].dataIndex);
48441             };
48442             dm.sortOrder = so;
48443             dm.load(dm.lastOptions);
48444             
48445             
48446         }
48447         
48448     },
48449
48450     updateCell : function(dm, rowIndex, dataIndex){
48451         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48452         if(typeof colIndex == "undefined"){ // not present in grid
48453             return;
48454         }
48455         var cm = this.grid.colModel;
48456         var cell = this.getCell(rowIndex, colIndex);
48457         var cellText = this.getCellText(rowIndex, colIndex);
48458
48459         var p = {
48460             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48461             id : cm.getColumnId(colIndex),
48462             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48463         };
48464         var renderer = cm.getRenderer(colIndex);
48465         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48466         if(typeof val == "undefined" || val === "") val = "&#160;";
48467         cellText.innerHTML = val;
48468         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48469         this.syncRowHeights(rowIndex, rowIndex);
48470     },
48471
48472     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48473         var maxWidth = 0;
48474         if(this.grid.autoSizeHeaders){
48475             var h = this.getHeaderCellMeasure(colIndex);
48476             maxWidth = Math.max(maxWidth, h.scrollWidth);
48477         }
48478         var tb, index;
48479         if(this.cm.isLocked(colIndex)){
48480             tb = this.getLockedTable();
48481             index = colIndex;
48482         }else{
48483             tb = this.getBodyTable();
48484             index = colIndex - this.cm.getLockedCount();
48485         }
48486         if(tb && tb.rows){
48487             var rows = tb.rows;
48488             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48489             for(var i = 0; i < stopIndex; i++){
48490                 var cell = rows[i].childNodes[index].firstChild;
48491                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48492             }
48493         }
48494         return maxWidth + /*margin for error in IE*/ 5;
48495     },
48496     /**
48497      * Autofit a column to its content.
48498      * @param {Number} colIndex
48499      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48500      */
48501      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48502          if(this.cm.isHidden(colIndex)){
48503              return; // can't calc a hidden column
48504          }
48505         if(forceMinSize){
48506             var cid = this.cm.getColumnId(colIndex);
48507             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48508            if(this.grid.autoSizeHeaders){
48509                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48510            }
48511         }
48512         var newWidth = this.calcColumnWidth(colIndex);
48513         this.cm.setColumnWidth(colIndex,
48514             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48515         if(!suppressEvent){
48516             this.grid.fireEvent("columnresize", colIndex, newWidth);
48517         }
48518     },
48519
48520     /**
48521      * Autofits all columns to their content and then expands to fit any extra space in the grid
48522      */
48523      autoSizeColumns : function(){
48524         var cm = this.grid.colModel;
48525         var colCount = cm.getColumnCount();
48526         for(var i = 0; i < colCount; i++){
48527             this.autoSizeColumn(i, true, true);
48528         }
48529         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48530             this.fitColumns();
48531         }else{
48532             this.updateColumns();
48533             this.layout();
48534         }
48535     },
48536
48537     /**
48538      * Autofits all columns to the grid's width proportionate with their current size
48539      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48540      */
48541     fitColumns : function(reserveScrollSpace){
48542         var cm = this.grid.colModel;
48543         var colCount = cm.getColumnCount();
48544         var cols = [];
48545         var width = 0;
48546         var i, w;
48547         for (i = 0; i < colCount; i++){
48548             if(!cm.isHidden(i) && !cm.isFixed(i)){
48549                 w = cm.getColumnWidth(i);
48550                 cols.push(i);
48551                 cols.push(w);
48552                 width += w;
48553             }
48554         }
48555         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48556         if(reserveScrollSpace){
48557             avail -= 17;
48558         }
48559         var frac = (avail - cm.getTotalWidth())/width;
48560         while (cols.length){
48561             w = cols.pop();
48562             i = cols.pop();
48563             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48564         }
48565         this.updateColumns();
48566         this.layout();
48567     },
48568
48569     onRowSelect : function(rowIndex){
48570         var row = this.getRowComposite(rowIndex);
48571         row.addClass("x-grid-row-selected");
48572     },
48573
48574     onRowDeselect : function(rowIndex){
48575         var row = this.getRowComposite(rowIndex);
48576         row.removeClass("x-grid-row-selected");
48577     },
48578
48579     onCellSelect : function(row, col){
48580         var cell = this.getCell(row, col);
48581         if(cell){
48582             Roo.fly(cell).addClass("x-grid-cell-selected");
48583         }
48584     },
48585
48586     onCellDeselect : function(row, col){
48587         var cell = this.getCell(row, col);
48588         if(cell){
48589             Roo.fly(cell).removeClass("x-grid-cell-selected");
48590         }
48591     },
48592
48593     updateHeaderSortState : function(){
48594         
48595         // sort state can be single { field: xxx, direction : yyy}
48596         // or   { xxx=>ASC , yyy : DESC ..... }
48597         
48598         var mstate = {};
48599         if (!this.ds.multiSort) { 
48600             var state = this.ds.getSortState();
48601             if(!state){
48602                 return;
48603             }
48604             mstate[state.field] = state.direction;
48605             // FIXME... - this is not used here.. but might be elsewhere..
48606             this.sortState = state;
48607             
48608         } else {
48609             mstate = this.ds.sortToggle;
48610         }
48611         //remove existing sort classes..
48612         
48613         var sc = this.sortClasses;
48614         var hds = this.el.select(this.headerSelector).removeClass(sc);
48615         
48616         for(var f in mstate) {
48617         
48618             var sortColumn = this.cm.findColumnIndex(f);
48619             
48620             if(sortColumn != -1){
48621                 var sortDir = mstate[f];        
48622                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48623             }
48624         }
48625         
48626          
48627         
48628     },
48629
48630
48631     handleHeaderClick : function(g, index){
48632         if(this.headersDisabled){
48633             return;
48634         }
48635         var dm = g.dataSource, cm = g.colModel;
48636         if(!cm.isSortable(index)){
48637             return;
48638         }
48639         g.stopEditing();
48640         
48641         if (dm.multiSort) {
48642             // update the sortOrder
48643             var so = [];
48644             for(var i = 0; i < cm.config.length; i++ ) {
48645                 
48646                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48647                     continue; // dont' bother, it's not in sort list or being set.
48648                 }
48649                 
48650                 so.push(cm.config[i].dataIndex);
48651             };
48652             dm.sortOrder = so;
48653         }
48654         
48655         
48656         dm.sort(cm.getDataIndex(index));
48657     },
48658
48659
48660     destroy : function(){
48661         if(this.colMenu){
48662             this.colMenu.removeAll();
48663             Roo.menu.MenuMgr.unregister(this.colMenu);
48664             this.colMenu.getEl().remove();
48665             delete this.colMenu;
48666         }
48667         if(this.hmenu){
48668             this.hmenu.removeAll();
48669             Roo.menu.MenuMgr.unregister(this.hmenu);
48670             this.hmenu.getEl().remove();
48671             delete this.hmenu;
48672         }
48673         if(this.grid.enableColumnMove){
48674             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48675             if(dds){
48676                 for(var dd in dds){
48677                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48678                         var elid = dds[dd].dragElId;
48679                         dds[dd].unreg();
48680                         Roo.get(elid).remove();
48681                     } else if(dds[dd].config.isTarget){
48682                         dds[dd].proxyTop.remove();
48683                         dds[dd].proxyBottom.remove();
48684                         dds[dd].unreg();
48685                     }
48686                     if(Roo.dd.DDM.locationCache[dd]){
48687                         delete Roo.dd.DDM.locationCache[dd];
48688                     }
48689                 }
48690                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48691             }
48692         }
48693         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48694         this.bind(null, null);
48695         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48696     },
48697
48698     handleLockChange : function(){
48699         this.refresh(true);
48700     },
48701
48702     onDenyColumnLock : function(){
48703
48704     },
48705
48706     onDenyColumnHide : function(){
48707
48708     },
48709
48710     handleHdMenuClick : function(item){
48711         var index = this.hdCtxIndex;
48712         var cm = this.cm, ds = this.ds;
48713         switch(item.id){
48714             case "asc":
48715                 ds.sort(cm.getDataIndex(index), "ASC");
48716                 break;
48717             case "desc":
48718                 ds.sort(cm.getDataIndex(index), "DESC");
48719                 break;
48720             case "lock":
48721                 var lc = cm.getLockedCount();
48722                 if(cm.getColumnCount(true) <= lc+1){
48723                     this.onDenyColumnLock();
48724                     return;
48725                 }
48726                 if(lc != index){
48727                     cm.setLocked(index, true, true);
48728                     cm.moveColumn(index, lc);
48729                     this.grid.fireEvent("columnmove", index, lc);
48730                 }else{
48731                     cm.setLocked(index, true);
48732                 }
48733             break;
48734             case "unlock":
48735                 var lc = cm.getLockedCount();
48736                 if((lc-1) != index){
48737                     cm.setLocked(index, false, true);
48738                     cm.moveColumn(index, lc-1);
48739                     this.grid.fireEvent("columnmove", index, lc-1);
48740                 }else{
48741                     cm.setLocked(index, false);
48742                 }
48743             break;
48744             default:
48745                 index = cm.getIndexById(item.id.substr(4));
48746                 if(index != -1){
48747                     if(item.checked && cm.getColumnCount(true) <= 1){
48748                         this.onDenyColumnHide();
48749                         return false;
48750                     }
48751                     cm.setHidden(index, item.checked);
48752                 }
48753         }
48754         return true;
48755     },
48756
48757     beforeColMenuShow : function(){
48758         var cm = this.cm,  colCount = cm.getColumnCount();
48759         this.colMenu.removeAll();
48760         for(var i = 0; i < colCount; i++){
48761             this.colMenu.add(new Roo.menu.CheckItem({
48762                 id: "col-"+cm.getColumnId(i),
48763                 text: cm.getColumnHeader(i),
48764                 checked: !cm.isHidden(i),
48765                 hideOnClick:false
48766             }));
48767         }
48768     },
48769
48770     handleHdCtx : function(g, index, e){
48771         e.stopEvent();
48772         var hd = this.getHeaderCell(index);
48773         this.hdCtxIndex = index;
48774         var ms = this.hmenu.items, cm = this.cm;
48775         ms.get("asc").setDisabled(!cm.isSortable(index));
48776         ms.get("desc").setDisabled(!cm.isSortable(index));
48777         if(this.grid.enableColLock !== false){
48778             ms.get("lock").setDisabled(cm.isLocked(index));
48779             ms.get("unlock").setDisabled(!cm.isLocked(index));
48780         }
48781         this.hmenu.show(hd, "tl-bl");
48782     },
48783
48784     handleHdOver : function(e){
48785         var hd = this.findHeaderCell(e.getTarget());
48786         if(hd && !this.headersDisabled){
48787             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48788                this.fly(hd).addClass("x-grid-hd-over");
48789             }
48790         }
48791     },
48792
48793     handleHdOut : function(e){
48794         var hd = this.findHeaderCell(e.getTarget());
48795         if(hd){
48796             this.fly(hd).removeClass("x-grid-hd-over");
48797         }
48798     },
48799
48800     handleSplitDblClick : function(e, t){
48801         var i = this.getCellIndex(t);
48802         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48803             this.autoSizeColumn(i, true);
48804             this.layout();
48805         }
48806     },
48807
48808     render : function(){
48809
48810         var cm = this.cm;
48811         var colCount = cm.getColumnCount();
48812
48813         if(this.grid.monitorWindowResize === true){
48814             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48815         }
48816         var header = this.renderHeaders();
48817         var body = this.templates.body.apply({rows:""});
48818         var html = this.templates.master.apply({
48819             lockedBody: body,
48820             body: body,
48821             lockedHeader: header[0],
48822             header: header[1]
48823         });
48824
48825         //this.updateColumns();
48826
48827         this.grid.getGridEl().dom.innerHTML = html;
48828
48829         this.initElements();
48830         
48831         // a kludge to fix the random scolling effect in webkit
48832         this.el.on("scroll", function() {
48833             this.el.dom.scrollTop=0; // hopefully not recursive..
48834         },this);
48835
48836         this.scroller.on("scroll", this.handleScroll, this);
48837         this.lockedBody.on("mousewheel", this.handleWheel, this);
48838         this.mainBody.on("mousewheel", this.handleWheel, this);
48839
48840         this.mainHd.on("mouseover", this.handleHdOver, this);
48841         this.mainHd.on("mouseout", this.handleHdOut, this);
48842         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48843                 {delegate: "."+this.splitClass});
48844
48845         this.lockedHd.on("mouseover", this.handleHdOver, this);
48846         this.lockedHd.on("mouseout", this.handleHdOut, this);
48847         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48848                 {delegate: "."+this.splitClass});
48849
48850         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48851             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48852         }
48853
48854         this.updateSplitters();
48855
48856         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48857             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48858             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48859         }
48860
48861         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48862             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48863             this.hmenu.add(
48864                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48865                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48866             );
48867             if(this.grid.enableColLock !== false){
48868                 this.hmenu.add('-',
48869                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48870                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48871                 );
48872             }
48873             if(this.grid.enableColumnHide !== false){
48874
48875                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48876                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48877                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48878
48879                 this.hmenu.add('-',
48880                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48881                 );
48882             }
48883             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48884
48885             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48886         }
48887
48888         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48889             this.dd = new Roo.grid.GridDragZone(this.grid, {
48890                 ddGroup : this.grid.ddGroup || 'GridDD'
48891             });
48892         }
48893
48894         /*
48895         for(var i = 0; i < colCount; i++){
48896             if(cm.isHidden(i)){
48897                 this.hideColumn(i);
48898             }
48899             if(cm.config[i].align){
48900                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48901                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48902             }
48903         }*/
48904         
48905         this.updateHeaderSortState();
48906
48907         this.beforeInitialResize();
48908         this.layout(true);
48909
48910         // two part rendering gives faster view to the user
48911         this.renderPhase2.defer(1, this);
48912     },
48913
48914     renderPhase2 : function(){
48915         // render the rows now
48916         this.refresh();
48917         if(this.grid.autoSizeColumns){
48918             this.autoSizeColumns();
48919         }
48920     },
48921
48922     beforeInitialResize : function(){
48923
48924     },
48925
48926     onColumnSplitterMoved : function(i, w){
48927         this.userResized = true;
48928         var cm = this.grid.colModel;
48929         cm.setColumnWidth(i, w, true);
48930         var cid = cm.getColumnId(i);
48931         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48932         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48933         this.updateSplitters();
48934         this.layout();
48935         this.grid.fireEvent("columnresize", i, w);
48936     },
48937
48938     syncRowHeights : function(startIndex, endIndex){
48939         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48940             startIndex = startIndex || 0;
48941             var mrows = this.getBodyTable().rows;
48942             var lrows = this.getLockedTable().rows;
48943             var len = mrows.length-1;
48944             endIndex = Math.min(endIndex || len, len);
48945             for(var i = startIndex; i <= endIndex; i++){
48946                 var m = mrows[i], l = lrows[i];
48947                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48948                 m.style.height = l.style.height = h + "px";
48949             }
48950         }
48951     },
48952
48953     layout : function(initialRender, is2ndPass){
48954         var g = this.grid;
48955         var auto = g.autoHeight;
48956         var scrollOffset = 16;
48957         var c = g.getGridEl(), cm = this.cm,
48958                 expandCol = g.autoExpandColumn,
48959                 gv = this;
48960         //c.beginMeasure();
48961
48962         if(!c.dom.offsetWidth){ // display:none?
48963             if(initialRender){
48964                 this.lockedWrap.show();
48965                 this.mainWrap.show();
48966             }
48967             return;
48968         }
48969
48970         var hasLock = this.cm.isLocked(0);
48971
48972         var tbh = this.headerPanel.getHeight();
48973         var bbh = this.footerPanel.getHeight();
48974
48975         if(auto){
48976             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48977             var newHeight = ch + c.getBorderWidth("tb");
48978             if(g.maxHeight){
48979                 newHeight = Math.min(g.maxHeight, newHeight);
48980             }
48981             c.setHeight(newHeight);
48982         }
48983
48984         if(g.autoWidth){
48985             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48986         }
48987
48988         var s = this.scroller;
48989
48990         var csize = c.getSize(true);
48991
48992         this.el.setSize(csize.width, csize.height);
48993
48994         this.headerPanel.setWidth(csize.width);
48995         this.footerPanel.setWidth(csize.width);
48996
48997         var hdHeight = this.mainHd.getHeight();
48998         var vw = csize.width;
48999         var vh = csize.height - (tbh + bbh);
49000
49001         s.setSize(vw, vh);
49002
49003         var bt = this.getBodyTable();
49004         var ltWidth = hasLock ?
49005                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49006
49007         var scrollHeight = bt.offsetHeight;
49008         var scrollWidth = ltWidth + bt.offsetWidth;
49009         var vscroll = false, hscroll = false;
49010
49011         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49012
49013         var lw = this.lockedWrap, mw = this.mainWrap;
49014         var lb = this.lockedBody, mb = this.mainBody;
49015
49016         setTimeout(function(){
49017             var t = s.dom.offsetTop;
49018             var w = s.dom.clientWidth,
49019                 h = s.dom.clientHeight;
49020
49021             lw.setTop(t);
49022             lw.setSize(ltWidth, h);
49023
49024             mw.setLeftTop(ltWidth, t);
49025             mw.setSize(w-ltWidth, h);
49026
49027             lb.setHeight(h-hdHeight);
49028             mb.setHeight(h-hdHeight);
49029
49030             if(is2ndPass !== true && !gv.userResized && expandCol){
49031                 // high speed resize without full column calculation
49032                 
49033                 var ci = cm.getIndexById(expandCol);
49034                 if (ci < 0) {
49035                     ci = cm.findColumnIndex(expandCol);
49036                 }
49037                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49038                 var expandId = cm.getColumnId(ci);
49039                 var  tw = cm.getTotalWidth(false);
49040                 var currentWidth = cm.getColumnWidth(ci);
49041                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49042                 if(currentWidth != cw){
49043                     cm.setColumnWidth(ci, cw, true);
49044                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49045                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49046                     gv.updateSplitters();
49047                     gv.layout(false, true);
49048                 }
49049             }
49050
49051             if(initialRender){
49052                 lw.show();
49053                 mw.show();
49054             }
49055             //c.endMeasure();
49056         }, 10);
49057     },
49058
49059     onWindowResize : function(){
49060         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49061             return;
49062         }
49063         this.layout();
49064     },
49065
49066     appendFooter : function(parentEl){
49067         return null;
49068     },
49069
49070     sortAscText : "Sort Ascending",
49071     sortDescText : "Sort Descending",
49072     lockText : "Lock Column",
49073     unlockText : "Unlock Column",
49074     columnsText : "Columns"
49075 });
49076
49077
49078 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49079     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49080     this.proxy.el.addClass('x-grid3-col-dd');
49081 };
49082
49083 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49084     handleMouseDown : function(e){
49085
49086     },
49087
49088     callHandleMouseDown : function(e){
49089         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49090     }
49091 });
49092 /*
49093  * Based on:
49094  * Ext JS Library 1.1.1
49095  * Copyright(c) 2006-2007, Ext JS, LLC.
49096  *
49097  * Originally Released Under LGPL - original licence link has changed is not relivant.
49098  *
49099  * Fork - LGPL
49100  * <script type="text/javascript">
49101  */
49102  
49103 // private
49104 // This is a support class used internally by the Grid components
49105 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49106     this.grid = grid;
49107     this.view = grid.getView();
49108     this.proxy = this.view.resizeProxy;
49109     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49110         "gridSplitters" + this.grid.getGridEl().id, {
49111         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49112     });
49113     this.setHandleElId(Roo.id(hd));
49114     this.setOuterHandleElId(Roo.id(hd2));
49115     this.scroll = false;
49116 };
49117 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49118     fly: Roo.Element.fly,
49119
49120     b4StartDrag : function(x, y){
49121         this.view.headersDisabled = true;
49122         this.proxy.setHeight(this.view.mainWrap.getHeight());
49123         var w = this.cm.getColumnWidth(this.cellIndex);
49124         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49125         this.resetConstraints();
49126         this.setXConstraint(minw, 1000);
49127         this.setYConstraint(0, 0);
49128         this.minX = x - minw;
49129         this.maxX = x + 1000;
49130         this.startPos = x;
49131         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49132     },
49133
49134
49135     handleMouseDown : function(e){
49136         ev = Roo.EventObject.setEvent(e);
49137         var t = this.fly(ev.getTarget());
49138         if(t.hasClass("x-grid-split")){
49139             this.cellIndex = this.view.getCellIndex(t.dom);
49140             this.split = t.dom;
49141             this.cm = this.grid.colModel;
49142             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49143                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49144             }
49145         }
49146     },
49147
49148     endDrag : function(e){
49149         this.view.headersDisabled = false;
49150         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49151         var diff = endX - this.startPos;
49152         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49153     },
49154
49155     autoOffset : function(){
49156         this.setDelta(0,0);
49157     }
49158 });/*
49159  * Based on:
49160  * Ext JS Library 1.1.1
49161  * Copyright(c) 2006-2007, Ext JS, LLC.
49162  *
49163  * Originally Released Under LGPL - original licence link has changed is not relivant.
49164  *
49165  * Fork - LGPL
49166  * <script type="text/javascript">
49167  */
49168  
49169 // private
49170 // This is a support class used internally by the Grid components
49171 Roo.grid.GridDragZone = function(grid, config){
49172     this.view = grid.getView();
49173     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49174     if(this.view.lockedBody){
49175         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49176         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49177     }
49178     this.scroll = false;
49179     this.grid = grid;
49180     this.ddel = document.createElement('div');
49181     this.ddel.className = 'x-grid-dd-wrap';
49182 };
49183
49184 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49185     ddGroup : "GridDD",
49186
49187     getDragData : function(e){
49188         var t = Roo.lib.Event.getTarget(e);
49189         var rowIndex = this.view.findRowIndex(t);
49190         if(rowIndex !== false){
49191             var sm = this.grid.selModel;
49192             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49193               //  sm.mouseDown(e, t);
49194             //}
49195             if (e.hasModifier()){
49196                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49197             }
49198             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49199         }
49200         return false;
49201     },
49202
49203     onInitDrag : function(e){
49204         var data = this.dragData;
49205         this.ddel.innerHTML = this.grid.getDragDropText();
49206         this.proxy.update(this.ddel);
49207         // fire start drag?
49208     },
49209
49210     afterRepair : function(){
49211         this.dragging = false;
49212     },
49213
49214     getRepairXY : function(e, data){
49215         return false;
49216     },
49217
49218     onEndDrag : function(data, e){
49219         // fire end drag?
49220     },
49221
49222     onValidDrop : function(dd, e, id){
49223         // fire drag drop?
49224         this.hideProxy();
49225     },
49226
49227     beforeInvalidDrop : function(e, id){
49228
49229     }
49230 });/*
49231  * Based on:
49232  * Ext JS Library 1.1.1
49233  * Copyright(c) 2006-2007, Ext JS, LLC.
49234  *
49235  * Originally Released Under LGPL - original licence link has changed is not relivant.
49236  *
49237  * Fork - LGPL
49238  * <script type="text/javascript">
49239  */
49240  
49241
49242 /**
49243  * @class Roo.grid.ColumnModel
49244  * @extends Roo.util.Observable
49245  * This is the default implementation of a ColumnModel used by the Grid. It defines
49246  * the columns in the grid.
49247  * <br>Usage:<br>
49248  <pre><code>
49249  var colModel = new Roo.grid.ColumnModel([
49250         {header: "Ticker", width: 60, sortable: true, locked: true},
49251         {header: "Company Name", width: 150, sortable: true},
49252         {header: "Market Cap.", width: 100, sortable: true},
49253         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49254         {header: "Employees", width: 100, sortable: true, resizable: false}
49255  ]);
49256  </code></pre>
49257  * <p>
49258  
49259  * The config options listed for this class are options which may appear in each
49260  * individual column definition.
49261  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49262  * @constructor
49263  * @param {Object} config An Array of column config objects. See this class's
49264  * config objects for details.
49265 */
49266 Roo.grid.ColumnModel = function(config){
49267         /**
49268      * The config passed into the constructor
49269      */
49270     this.config = config;
49271     this.lookup = {};
49272
49273     // if no id, create one
49274     // if the column does not have a dataIndex mapping,
49275     // map it to the order it is in the config
49276     for(var i = 0, len = config.length; i < len; i++){
49277         var c = config[i];
49278         if(typeof c.dataIndex == "undefined"){
49279             c.dataIndex = i;
49280         }
49281         if(typeof c.renderer == "string"){
49282             c.renderer = Roo.util.Format[c.renderer];
49283         }
49284         if(typeof c.id == "undefined"){
49285             c.id = Roo.id();
49286         }
49287         if(c.editor && c.editor.xtype){
49288             c.editor  = Roo.factory(c.editor, Roo.grid);
49289         }
49290         if(c.editor && c.editor.isFormField){
49291             c.editor = new Roo.grid.GridEditor(c.editor);
49292         }
49293         this.lookup[c.id] = c;
49294     }
49295
49296     /**
49297      * The width of columns which have no width specified (defaults to 100)
49298      * @type Number
49299      */
49300     this.defaultWidth = 100;
49301
49302     /**
49303      * Default sortable of columns which have no sortable specified (defaults to false)
49304      * @type Boolean
49305      */
49306     this.defaultSortable = false;
49307
49308     this.addEvents({
49309         /**
49310              * @event widthchange
49311              * Fires when the width of a column changes.
49312              * @param {ColumnModel} this
49313              * @param {Number} columnIndex The column index
49314              * @param {Number} newWidth The new width
49315              */
49316             "widthchange": true,
49317         /**
49318              * @event headerchange
49319              * Fires when the text of a header changes.
49320              * @param {ColumnModel} this
49321              * @param {Number} columnIndex The column index
49322              * @param {Number} newText The new header text
49323              */
49324             "headerchange": true,
49325         /**
49326              * @event hiddenchange
49327              * Fires when a column is hidden or "unhidden".
49328              * @param {ColumnModel} this
49329              * @param {Number} columnIndex The column index
49330              * @param {Boolean} hidden true if hidden, false otherwise
49331              */
49332             "hiddenchange": true,
49333             /**
49334          * @event columnmoved
49335          * Fires when a column is moved.
49336          * @param {ColumnModel} this
49337          * @param {Number} oldIndex
49338          * @param {Number} newIndex
49339          */
49340         "columnmoved" : true,
49341         /**
49342          * @event columlockchange
49343          * Fires when a column's locked state is changed
49344          * @param {ColumnModel} this
49345          * @param {Number} colIndex
49346          * @param {Boolean} locked true if locked
49347          */
49348         "columnlockchange" : true
49349     });
49350     Roo.grid.ColumnModel.superclass.constructor.call(this);
49351 };
49352 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49353     /**
49354      * @cfg {String} header The header text to display in the Grid view.
49355      */
49356     /**
49357      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49358      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49359      * specified, the column's index is used as an index into the Record's data Array.
49360      */
49361     /**
49362      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49363      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49364      */
49365     /**
49366      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49367      * Defaults to the value of the {@link #defaultSortable} property.
49368      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49369      */
49370     /**
49371      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49372      */
49373     /**
49374      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49375      */
49376     /**
49377      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49378      */
49379     /**
49380      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49381      */
49382     /**
49383      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49384      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49385      * default renderer uses the raw data value.
49386      */
49387        /**
49388      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49389      */
49390     /**
49391      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49392      */
49393
49394     /**
49395      * Returns the id of the column at the specified index.
49396      * @param {Number} index The column index
49397      * @return {String} the id
49398      */
49399     getColumnId : function(index){
49400         return this.config[index].id;
49401     },
49402
49403     /**
49404      * Returns the column for a specified id.
49405      * @param {String} id The column id
49406      * @return {Object} the column
49407      */
49408     getColumnById : function(id){
49409         return this.lookup[id];
49410     },
49411
49412     
49413     /**
49414      * Returns the column for a specified dataIndex.
49415      * @param {String} dataIndex The column dataIndex
49416      * @return {Object|Boolean} the column or false if not found
49417      */
49418     getColumnByDataIndex: function(dataIndex){
49419         var index = this.findColumnIndex(dataIndex);
49420         return index > -1 ? this.config[index] : false;
49421     },
49422     
49423     /**
49424      * Returns the index for a specified column id.
49425      * @param {String} id The column id
49426      * @return {Number} the index, or -1 if not found
49427      */
49428     getIndexById : function(id){
49429         for(var i = 0, len = this.config.length; i < len; i++){
49430             if(this.config[i].id == id){
49431                 return i;
49432             }
49433         }
49434         return -1;
49435     },
49436     
49437     /**
49438      * Returns the index for a specified column dataIndex.
49439      * @param {String} dataIndex The column dataIndex
49440      * @return {Number} the index, or -1 if not found
49441      */
49442     
49443     findColumnIndex : function(dataIndex){
49444         for(var i = 0, len = this.config.length; i < len; i++){
49445             if(this.config[i].dataIndex == dataIndex){
49446                 return i;
49447             }
49448         }
49449         return -1;
49450     },
49451     
49452     
49453     moveColumn : function(oldIndex, newIndex){
49454         var c = this.config[oldIndex];
49455         this.config.splice(oldIndex, 1);
49456         this.config.splice(newIndex, 0, c);
49457         this.dataMap = null;
49458         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49459     },
49460
49461     isLocked : function(colIndex){
49462         return this.config[colIndex].locked === true;
49463     },
49464
49465     setLocked : function(colIndex, value, suppressEvent){
49466         if(this.isLocked(colIndex) == value){
49467             return;
49468         }
49469         this.config[colIndex].locked = value;
49470         if(!suppressEvent){
49471             this.fireEvent("columnlockchange", this, colIndex, value);
49472         }
49473     },
49474
49475     getTotalLockedWidth : function(){
49476         var totalWidth = 0;
49477         for(var i = 0; i < this.config.length; i++){
49478             if(this.isLocked(i) && !this.isHidden(i)){
49479                 this.totalWidth += this.getColumnWidth(i);
49480             }
49481         }
49482         return totalWidth;
49483     },
49484
49485     getLockedCount : function(){
49486         for(var i = 0, len = this.config.length; i < len; i++){
49487             if(!this.isLocked(i)){
49488                 return i;
49489             }
49490         }
49491     },
49492
49493     /**
49494      * Returns the number of columns.
49495      * @return {Number}
49496      */
49497     getColumnCount : function(visibleOnly){
49498         if(visibleOnly === true){
49499             var c = 0;
49500             for(var i = 0, len = this.config.length; i < len; i++){
49501                 if(!this.isHidden(i)){
49502                     c++;
49503                 }
49504             }
49505             return c;
49506         }
49507         return this.config.length;
49508     },
49509
49510     /**
49511      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49512      * @param {Function} fn
49513      * @param {Object} scope (optional)
49514      * @return {Array} result
49515      */
49516     getColumnsBy : function(fn, scope){
49517         var r = [];
49518         for(var i = 0, len = this.config.length; i < len; i++){
49519             var c = this.config[i];
49520             if(fn.call(scope||this, c, i) === true){
49521                 r[r.length] = c;
49522             }
49523         }
49524         return r;
49525     },
49526
49527     /**
49528      * Returns true if the specified column is sortable.
49529      * @param {Number} col The column index
49530      * @return {Boolean}
49531      */
49532     isSortable : function(col){
49533         if(typeof this.config[col].sortable == "undefined"){
49534             return this.defaultSortable;
49535         }
49536         return this.config[col].sortable;
49537     },
49538
49539     /**
49540      * Returns the rendering (formatting) function defined for the column.
49541      * @param {Number} col The column index.
49542      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49543      */
49544     getRenderer : function(col){
49545         if(!this.config[col].renderer){
49546             return Roo.grid.ColumnModel.defaultRenderer;
49547         }
49548         return this.config[col].renderer;
49549     },
49550
49551     /**
49552      * Sets the rendering (formatting) function for a column.
49553      * @param {Number} col The column index
49554      * @param {Function} fn The function to use to process the cell's raw data
49555      * to return HTML markup for the grid view. The render function is called with
49556      * the following parameters:<ul>
49557      * <li>Data value.</li>
49558      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49559      * <li>css A CSS style string to apply to the table cell.</li>
49560      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49561      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49562      * <li>Row index</li>
49563      * <li>Column index</li>
49564      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49565      */
49566     setRenderer : function(col, fn){
49567         this.config[col].renderer = fn;
49568     },
49569
49570     /**
49571      * Returns the width for the specified column.
49572      * @param {Number} col The column index
49573      * @return {Number}
49574      */
49575     getColumnWidth : function(col){
49576         return this.config[col].width * 1 || this.defaultWidth;
49577     },
49578
49579     /**
49580      * Sets the width for a column.
49581      * @param {Number} col The column index
49582      * @param {Number} width The new width
49583      */
49584     setColumnWidth : function(col, width, suppressEvent){
49585         this.config[col].width = width;
49586         this.totalWidth = null;
49587         if(!suppressEvent){
49588              this.fireEvent("widthchange", this, col, width);
49589         }
49590     },
49591
49592     /**
49593      * Returns the total width of all columns.
49594      * @param {Boolean} includeHidden True to include hidden column widths
49595      * @return {Number}
49596      */
49597     getTotalWidth : function(includeHidden){
49598         if(!this.totalWidth){
49599             this.totalWidth = 0;
49600             for(var i = 0, len = this.config.length; i < len; i++){
49601                 if(includeHidden || !this.isHidden(i)){
49602                     this.totalWidth += this.getColumnWidth(i);
49603                 }
49604             }
49605         }
49606         return this.totalWidth;
49607     },
49608
49609     /**
49610      * Returns the header for the specified column.
49611      * @param {Number} col The column index
49612      * @return {String}
49613      */
49614     getColumnHeader : function(col){
49615         return this.config[col].header;
49616     },
49617
49618     /**
49619      * Sets the header for a column.
49620      * @param {Number} col The column index
49621      * @param {String} header The new header
49622      */
49623     setColumnHeader : function(col, header){
49624         this.config[col].header = header;
49625         this.fireEvent("headerchange", this, col, header);
49626     },
49627
49628     /**
49629      * Returns the tooltip for the specified column.
49630      * @param {Number} col The column index
49631      * @return {String}
49632      */
49633     getColumnTooltip : function(col){
49634             return this.config[col].tooltip;
49635     },
49636     /**
49637      * Sets the tooltip for a column.
49638      * @param {Number} col The column index
49639      * @param {String} tooltip The new tooltip
49640      */
49641     setColumnTooltip : function(col, tooltip){
49642             this.config[col].tooltip = tooltip;
49643     },
49644
49645     /**
49646      * Returns the dataIndex for the specified column.
49647      * @param {Number} col The column index
49648      * @return {Number}
49649      */
49650     getDataIndex : function(col){
49651         return this.config[col].dataIndex;
49652     },
49653
49654     /**
49655      * Sets the dataIndex for a column.
49656      * @param {Number} col The column index
49657      * @param {Number} dataIndex The new dataIndex
49658      */
49659     setDataIndex : function(col, dataIndex){
49660         this.config[col].dataIndex = dataIndex;
49661     },
49662
49663     
49664     
49665     /**
49666      * Returns true if the cell is editable.
49667      * @param {Number} colIndex The column index
49668      * @param {Number} rowIndex The row index
49669      * @return {Boolean}
49670      */
49671     isCellEditable : function(colIndex, rowIndex){
49672         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49673     },
49674
49675     /**
49676      * Returns the editor defined for the cell/column.
49677      * return false or null to disable editing.
49678      * @param {Number} colIndex The column index
49679      * @param {Number} rowIndex The row index
49680      * @return {Object}
49681      */
49682     getCellEditor : function(colIndex, rowIndex){
49683         return this.config[colIndex].editor;
49684     },
49685
49686     /**
49687      * Sets if a column is editable.
49688      * @param {Number} col The column index
49689      * @param {Boolean} editable True if the column is editable
49690      */
49691     setEditable : function(col, editable){
49692         this.config[col].editable = editable;
49693     },
49694
49695
49696     /**
49697      * Returns true if the column is hidden.
49698      * @param {Number} colIndex The column index
49699      * @return {Boolean}
49700      */
49701     isHidden : function(colIndex){
49702         return this.config[colIndex].hidden;
49703     },
49704
49705
49706     /**
49707      * Returns true if the column width cannot be changed
49708      */
49709     isFixed : function(colIndex){
49710         return this.config[colIndex].fixed;
49711     },
49712
49713     /**
49714      * Returns true if the column can be resized
49715      * @return {Boolean}
49716      */
49717     isResizable : function(colIndex){
49718         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49719     },
49720     /**
49721      * Sets if a column is hidden.
49722      * @param {Number} colIndex The column index
49723      * @param {Boolean} hidden True if the column is hidden
49724      */
49725     setHidden : function(colIndex, hidden){
49726         this.config[colIndex].hidden = hidden;
49727         this.totalWidth = null;
49728         this.fireEvent("hiddenchange", this, colIndex, hidden);
49729     },
49730
49731     /**
49732      * Sets the editor for a column.
49733      * @param {Number} col The column index
49734      * @param {Object} editor The editor object
49735      */
49736     setEditor : function(col, editor){
49737         this.config[col].editor = editor;
49738     }
49739 });
49740
49741 Roo.grid.ColumnModel.defaultRenderer = function(value){
49742         if(typeof value == "string" && value.length < 1){
49743             return "&#160;";
49744         }
49745         return value;
49746 };
49747
49748 // Alias for backwards compatibility
49749 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49750 /*
49751  * Based on:
49752  * Ext JS Library 1.1.1
49753  * Copyright(c) 2006-2007, Ext JS, LLC.
49754  *
49755  * Originally Released Under LGPL - original licence link has changed is not relivant.
49756  *
49757  * Fork - LGPL
49758  * <script type="text/javascript">
49759  */
49760
49761 /**
49762  * @class Roo.grid.AbstractSelectionModel
49763  * @extends Roo.util.Observable
49764  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49765  * implemented by descendant classes.  This class should not be directly instantiated.
49766  * @constructor
49767  */
49768 Roo.grid.AbstractSelectionModel = function(){
49769     this.locked = false;
49770     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49771 };
49772
49773 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49774     /** @ignore Called by the grid automatically. Do not call directly. */
49775     init : function(grid){
49776         this.grid = grid;
49777         this.initEvents();
49778     },
49779
49780     /**
49781      * Locks the selections.
49782      */
49783     lock : function(){
49784         this.locked = true;
49785     },
49786
49787     /**
49788      * Unlocks the selections.
49789      */
49790     unlock : function(){
49791         this.locked = false;
49792     },
49793
49794     /**
49795      * Returns true if the selections are locked.
49796      * @return {Boolean}
49797      */
49798     isLocked : function(){
49799         return this.locked;
49800     }
49801 });/*
49802  * Based on:
49803  * Ext JS Library 1.1.1
49804  * Copyright(c) 2006-2007, Ext JS, LLC.
49805  *
49806  * Originally Released Under LGPL - original licence link has changed is not relivant.
49807  *
49808  * Fork - LGPL
49809  * <script type="text/javascript">
49810  */
49811 /**
49812  * @extends Roo.grid.AbstractSelectionModel
49813  * @class Roo.grid.RowSelectionModel
49814  * The default SelectionModel used by {@link Roo.grid.Grid}.
49815  * It supports multiple selections and keyboard selection/navigation. 
49816  * @constructor
49817  * @param {Object} config
49818  */
49819 Roo.grid.RowSelectionModel = function(config){
49820     Roo.apply(this, config);
49821     this.selections = new Roo.util.MixedCollection(false, function(o){
49822         return o.id;
49823     });
49824
49825     this.last = false;
49826     this.lastActive = false;
49827
49828     this.addEvents({
49829         /**
49830              * @event selectionchange
49831              * Fires when the selection changes
49832              * @param {SelectionModel} this
49833              */
49834             "selectionchange" : true,
49835         /**
49836              * @event afterselectionchange
49837              * Fires after the selection changes (eg. by key press or clicking)
49838              * @param {SelectionModel} this
49839              */
49840             "afterselectionchange" : true,
49841         /**
49842              * @event beforerowselect
49843              * Fires when a row is selected being selected, return false to cancel.
49844              * @param {SelectionModel} this
49845              * @param {Number} rowIndex The selected index
49846              * @param {Boolean} keepExisting False if other selections will be cleared
49847              */
49848             "beforerowselect" : true,
49849         /**
49850              * @event rowselect
49851              * Fires when a row is selected.
49852              * @param {SelectionModel} this
49853              * @param {Number} rowIndex The selected index
49854              * @param {Roo.data.Record} r The record
49855              */
49856             "rowselect" : true,
49857         /**
49858              * @event rowdeselect
49859              * Fires when a row is deselected.
49860              * @param {SelectionModel} this
49861              * @param {Number} rowIndex The selected index
49862              */
49863         "rowdeselect" : true
49864     });
49865     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49866     this.locked = false;
49867 };
49868
49869 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49870     /**
49871      * @cfg {Boolean} singleSelect
49872      * True to allow selection of only one row at a time (defaults to false)
49873      */
49874     singleSelect : false,
49875
49876     // private
49877     initEvents : function(){
49878
49879         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49880             this.grid.on("mousedown", this.handleMouseDown, this);
49881         }else{ // allow click to work like normal
49882             this.grid.on("rowclick", this.handleDragableRowClick, this);
49883         }
49884
49885         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49886             "up" : function(e){
49887                 if(!e.shiftKey){
49888                     this.selectPrevious(e.shiftKey);
49889                 }else if(this.last !== false && this.lastActive !== false){
49890                     var last = this.last;
49891                     this.selectRange(this.last,  this.lastActive-1);
49892                     this.grid.getView().focusRow(this.lastActive);
49893                     if(last !== false){
49894                         this.last = last;
49895                     }
49896                 }else{
49897                     this.selectFirstRow();
49898                 }
49899                 this.fireEvent("afterselectionchange", this);
49900             },
49901             "down" : function(e){
49902                 if(!e.shiftKey){
49903                     this.selectNext(e.shiftKey);
49904                 }else if(this.last !== false && this.lastActive !== false){
49905                     var last = this.last;
49906                     this.selectRange(this.last,  this.lastActive+1);
49907                     this.grid.getView().focusRow(this.lastActive);
49908                     if(last !== false){
49909                         this.last = last;
49910                     }
49911                 }else{
49912                     this.selectFirstRow();
49913                 }
49914                 this.fireEvent("afterselectionchange", this);
49915             },
49916             scope: this
49917         });
49918
49919         var view = this.grid.view;
49920         view.on("refresh", this.onRefresh, this);
49921         view.on("rowupdated", this.onRowUpdated, this);
49922         view.on("rowremoved", this.onRemove, this);
49923     },
49924
49925     // private
49926     onRefresh : function(){
49927         var ds = this.grid.dataSource, i, v = this.grid.view;
49928         var s = this.selections;
49929         s.each(function(r){
49930             if((i = ds.indexOfId(r.id)) != -1){
49931                 v.onRowSelect(i);
49932             }else{
49933                 s.remove(r);
49934             }
49935         });
49936     },
49937
49938     // private
49939     onRemove : function(v, index, r){
49940         this.selections.remove(r);
49941     },
49942
49943     // private
49944     onRowUpdated : function(v, index, r){
49945         if(this.isSelected(r)){
49946             v.onRowSelect(index);
49947         }
49948     },
49949
49950     /**
49951      * Select records.
49952      * @param {Array} records The records to select
49953      * @param {Boolean} keepExisting (optional) True to keep existing selections
49954      */
49955     selectRecords : function(records, keepExisting){
49956         if(!keepExisting){
49957             this.clearSelections();
49958         }
49959         var ds = this.grid.dataSource;
49960         for(var i = 0, len = records.length; i < len; i++){
49961             this.selectRow(ds.indexOf(records[i]), true);
49962         }
49963     },
49964
49965     /**
49966      * Gets the number of selected rows.
49967      * @return {Number}
49968      */
49969     getCount : function(){
49970         return this.selections.length;
49971     },
49972
49973     /**
49974      * Selects the first row in the grid.
49975      */
49976     selectFirstRow : function(){
49977         this.selectRow(0);
49978     },
49979
49980     /**
49981      * Select the last row.
49982      * @param {Boolean} keepExisting (optional) True to keep existing selections
49983      */
49984     selectLastRow : function(keepExisting){
49985         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49986     },
49987
49988     /**
49989      * Selects the row immediately following the last selected row.
49990      * @param {Boolean} keepExisting (optional) True to keep existing selections
49991      */
49992     selectNext : function(keepExisting){
49993         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49994             this.selectRow(this.last+1, keepExisting);
49995             this.grid.getView().focusRow(this.last);
49996         }
49997     },
49998
49999     /**
50000      * Selects the row that precedes the last selected row.
50001      * @param {Boolean} keepExisting (optional) True to keep existing selections
50002      */
50003     selectPrevious : function(keepExisting){
50004         if(this.last){
50005             this.selectRow(this.last-1, keepExisting);
50006             this.grid.getView().focusRow(this.last);
50007         }
50008     },
50009
50010     /**
50011      * Returns the selected records
50012      * @return {Array} Array of selected records
50013      */
50014     getSelections : function(){
50015         return [].concat(this.selections.items);
50016     },
50017
50018     /**
50019      * Returns the first selected record.
50020      * @return {Record}
50021      */
50022     getSelected : function(){
50023         return this.selections.itemAt(0);
50024     },
50025
50026
50027     /**
50028      * Clears all selections.
50029      */
50030     clearSelections : function(fast){
50031         if(this.locked) return;
50032         if(fast !== true){
50033             var ds = this.grid.dataSource;
50034             var s = this.selections;
50035             s.each(function(r){
50036                 this.deselectRow(ds.indexOfId(r.id));
50037             }, this);
50038             s.clear();
50039         }else{
50040             this.selections.clear();
50041         }
50042         this.last = false;
50043     },
50044
50045
50046     /**
50047      * Selects all rows.
50048      */
50049     selectAll : function(){
50050         if(this.locked) return;
50051         this.selections.clear();
50052         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50053             this.selectRow(i, true);
50054         }
50055     },
50056
50057     /**
50058      * Returns True if there is a selection.
50059      * @return {Boolean}
50060      */
50061     hasSelection : function(){
50062         return this.selections.length > 0;
50063     },
50064
50065     /**
50066      * Returns True if the specified row is selected.
50067      * @param {Number/Record} record The record or index of the record to check
50068      * @return {Boolean}
50069      */
50070     isSelected : function(index){
50071         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50072         return (r && this.selections.key(r.id) ? true : false);
50073     },
50074
50075     /**
50076      * Returns True if the specified record id is selected.
50077      * @param {String} id The id of record to check
50078      * @return {Boolean}
50079      */
50080     isIdSelected : function(id){
50081         return (this.selections.key(id) ? true : false);
50082     },
50083
50084     // private
50085     handleMouseDown : function(e, t){
50086         var view = this.grid.getView(), rowIndex;
50087         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50088             return;
50089         };
50090         if(e.shiftKey && this.last !== false){
50091             var last = this.last;
50092             this.selectRange(last, rowIndex, e.ctrlKey);
50093             this.last = last; // reset the last
50094             view.focusRow(rowIndex);
50095         }else{
50096             var isSelected = this.isSelected(rowIndex);
50097             if(e.button !== 0 && isSelected){
50098                 view.focusRow(rowIndex);
50099             }else if(e.ctrlKey && isSelected){
50100                 this.deselectRow(rowIndex);
50101             }else if(!isSelected){
50102                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50103                 view.focusRow(rowIndex);
50104             }
50105         }
50106         this.fireEvent("afterselectionchange", this);
50107     },
50108     // private
50109     handleDragableRowClick :  function(grid, rowIndex, e) 
50110     {
50111         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50112             this.selectRow(rowIndex, false);
50113             grid.view.focusRow(rowIndex);
50114              this.fireEvent("afterselectionchange", this);
50115         }
50116     },
50117     
50118     /**
50119      * Selects multiple rows.
50120      * @param {Array} rows Array of the indexes of the row to select
50121      * @param {Boolean} keepExisting (optional) True to keep existing selections
50122      */
50123     selectRows : function(rows, keepExisting){
50124         if(!keepExisting){
50125             this.clearSelections();
50126         }
50127         for(var i = 0, len = rows.length; i < len; i++){
50128             this.selectRow(rows[i], true);
50129         }
50130     },
50131
50132     /**
50133      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50134      * @param {Number} startRow The index of the first row in the range
50135      * @param {Number} endRow The index of the last row in the range
50136      * @param {Boolean} keepExisting (optional) True to retain existing selections
50137      */
50138     selectRange : function(startRow, endRow, keepExisting){
50139         if(this.locked) return;
50140         if(!keepExisting){
50141             this.clearSelections();
50142         }
50143         if(startRow <= endRow){
50144             for(var i = startRow; i <= endRow; i++){
50145                 this.selectRow(i, true);
50146             }
50147         }else{
50148             for(var i = startRow; i >= endRow; i--){
50149                 this.selectRow(i, true);
50150             }
50151         }
50152     },
50153
50154     /**
50155      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50156      * @param {Number} startRow The index of the first row in the range
50157      * @param {Number} endRow The index of the last row in the range
50158      */
50159     deselectRange : function(startRow, endRow, preventViewNotify){
50160         if(this.locked) return;
50161         for(var i = startRow; i <= endRow; i++){
50162             this.deselectRow(i, preventViewNotify);
50163         }
50164     },
50165
50166     /**
50167      * Selects a row.
50168      * @param {Number} row The index of the row to select
50169      * @param {Boolean} keepExisting (optional) True to keep existing selections
50170      */
50171     selectRow : function(index, keepExisting, preventViewNotify){
50172         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50173         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50174             if(!keepExisting || this.singleSelect){
50175                 this.clearSelections();
50176             }
50177             var r = this.grid.dataSource.getAt(index);
50178             this.selections.add(r);
50179             this.last = this.lastActive = index;
50180             if(!preventViewNotify){
50181                 this.grid.getView().onRowSelect(index);
50182             }
50183             this.fireEvent("rowselect", this, index, r);
50184             this.fireEvent("selectionchange", this);
50185         }
50186     },
50187
50188     /**
50189      * Deselects a row.
50190      * @param {Number} row The index of the row to deselect
50191      */
50192     deselectRow : function(index, preventViewNotify){
50193         if(this.locked) return;
50194         if(this.last == index){
50195             this.last = false;
50196         }
50197         if(this.lastActive == index){
50198             this.lastActive = false;
50199         }
50200         var r = this.grid.dataSource.getAt(index);
50201         this.selections.remove(r);
50202         if(!preventViewNotify){
50203             this.grid.getView().onRowDeselect(index);
50204         }
50205         this.fireEvent("rowdeselect", this, index);
50206         this.fireEvent("selectionchange", this);
50207     },
50208
50209     // private
50210     restoreLast : function(){
50211         if(this._last){
50212             this.last = this._last;
50213         }
50214     },
50215
50216     // private
50217     acceptsNav : function(row, col, cm){
50218         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50219     },
50220
50221     // private
50222     onEditorKey : function(field, e){
50223         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50224         if(k == e.TAB){
50225             e.stopEvent();
50226             ed.completeEdit();
50227             if(e.shiftKey){
50228                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50229             }else{
50230                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50231             }
50232         }else if(k == e.ENTER && !e.ctrlKey){
50233             e.stopEvent();
50234             ed.completeEdit();
50235             if(e.shiftKey){
50236                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50237             }else{
50238                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50239             }
50240         }else if(k == e.ESC){
50241             ed.cancelEdit();
50242         }
50243         if(newCell){
50244             g.startEditing(newCell[0], newCell[1]);
50245         }
50246     }
50247 });/*
50248  * Based on:
50249  * Ext JS Library 1.1.1
50250  * Copyright(c) 2006-2007, Ext JS, LLC.
50251  *
50252  * Originally Released Under LGPL - original licence link has changed is not relivant.
50253  *
50254  * Fork - LGPL
50255  * <script type="text/javascript">
50256  */
50257 /**
50258  * @class Roo.grid.CellSelectionModel
50259  * @extends Roo.grid.AbstractSelectionModel
50260  * This class provides the basic implementation for cell selection in a grid.
50261  * @constructor
50262  * @param {Object} config The object containing the configuration of this model.
50263  */
50264 Roo.grid.CellSelectionModel = function(config){
50265     Roo.apply(this, config);
50266
50267     this.selection = null;
50268
50269     this.addEvents({
50270         /**
50271              * @event beforerowselect
50272              * Fires before a cell is selected.
50273              * @param {SelectionModel} this
50274              * @param {Number} rowIndex The selected row index
50275              * @param {Number} colIndex The selected cell index
50276              */
50277             "beforecellselect" : true,
50278         /**
50279              * @event cellselect
50280              * Fires when a cell is selected.
50281              * @param {SelectionModel} this
50282              * @param {Number} rowIndex The selected row index
50283              * @param {Number} colIndex The selected cell index
50284              */
50285             "cellselect" : true,
50286         /**
50287              * @event selectionchange
50288              * Fires when the active selection changes.
50289              * @param {SelectionModel} this
50290              * @param {Object} selection null for no selection or an object (o) with two properties
50291                 <ul>
50292                 <li>o.record: the record object for the row the selection is in</li>
50293                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50294                 </ul>
50295              */
50296             "selectionchange" : true
50297     });
50298     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50299 };
50300
50301 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50302
50303     /** @ignore */
50304     initEvents : function(){
50305         this.grid.on("mousedown", this.handleMouseDown, this);
50306         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50307         var view = this.grid.view;
50308         view.on("refresh", this.onViewChange, this);
50309         view.on("rowupdated", this.onRowUpdated, this);
50310         view.on("beforerowremoved", this.clearSelections, this);
50311         view.on("beforerowsinserted", this.clearSelections, this);
50312         if(this.grid.isEditor){
50313             this.grid.on("beforeedit", this.beforeEdit,  this);
50314         }
50315     },
50316
50317         //private
50318     beforeEdit : function(e){
50319         this.select(e.row, e.column, false, true, e.record);
50320     },
50321
50322         //private
50323     onRowUpdated : function(v, index, r){
50324         if(this.selection && this.selection.record == r){
50325             v.onCellSelect(index, this.selection.cell[1]);
50326         }
50327     },
50328
50329         //private
50330     onViewChange : function(){
50331         this.clearSelections(true);
50332     },
50333
50334         /**
50335          * Returns the currently selected cell,.
50336          * @return {Array} The selected cell (row, column) or null if none selected.
50337          */
50338     getSelectedCell : function(){
50339         return this.selection ? this.selection.cell : null;
50340     },
50341
50342     /**
50343      * Clears all selections.
50344      * @param {Boolean} true to prevent the gridview from being notified about the change.
50345      */
50346     clearSelections : function(preventNotify){
50347         var s = this.selection;
50348         if(s){
50349             if(preventNotify !== true){
50350                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50351             }
50352             this.selection = null;
50353             this.fireEvent("selectionchange", this, null);
50354         }
50355     },
50356
50357     /**
50358      * Returns true if there is a selection.
50359      * @return {Boolean}
50360      */
50361     hasSelection : function(){
50362         return this.selection ? true : false;
50363     },
50364
50365     /** @ignore */
50366     handleMouseDown : function(e, t){
50367         var v = this.grid.getView();
50368         if(this.isLocked()){
50369             return;
50370         };
50371         var row = v.findRowIndex(t);
50372         var cell = v.findCellIndex(t);
50373         if(row !== false && cell !== false){
50374             this.select(row, cell);
50375         }
50376     },
50377
50378     /**
50379      * Selects a cell.
50380      * @param {Number} rowIndex
50381      * @param {Number} collIndex
50382      */
50383     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50384         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50385             this.clearSelections();
50386             r = r || this.grid.dataSource.getAt(rowIndex);
50387             this.selection = {
50388                 record : r,
50389                 cell : [rowIndex, colIndex]
50390             };
50391             if(!preventViewNotify){
50392                 var v = this.grid.getView();
50393                 v.onCellSelect(rowIndex, colIndex);
50394                 if(preventFocus !== true){
50395                     v.focusCell(rowIndex, colIndex);
50396                 }
50397             }
50398             this.fireEvent("cellselect", this, rowIndex, colIndex);
50399             this.fireEvent("selectionchange", this, this.selection);
50400         }
50401     },
50402
50403         //private
50404     isSelectable : function(rowIndex, colIndex, cm){
50405         return !cm.isHidden(colIndex);
50406     },
50407
50408     /** @ignore */
50409     handleKeyDown : function(e){
50410         Roo.log('Cell Sel Model handleKeyDown');
50411         if(!e.isNavKeyPress()){
50412             return;
50413         }
50414         var g = this.grid, s = this.selection;
50415         if(!s){
50416             e.stopEvent();
50417             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50418             if(cell){
50419                 this.select(cell[0], cell[1]);
50420             }
50421             return;
50422         }
50423         var sm = this;
50424         var walk = function(row, col, step){
50425             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50426         };
50427         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50428         var newCell;
50429
50430         switch(k){
50431             case e.TAB:
50432                 // handled by onEditorKey
50433                 if (g.isEditor && g.editing) {
50434                     return;
50435                 }
50436                 if(e.shiftKey){
50437                      newCell = walk(r, c-1, -1);
50438                 }else{
50439                      newCell = walk(r, c+1, 1);
50440                 }
50441              break;
50442              case e.DOWN:
50443                  newCell = walk(r+1, c, 1);
50444              break;
50445              case e.UP:
50446                  newCell = walk(r-1, c, -1);
50447              break;
50448              case e.RIGHT:
50449                  newCell = walk(r, c+1, 1);
50450              break;
50451              case e.LEFT:
50452                  newCell = walk(r, c-1, -1);
50453              break;
50454              case e.ENTER:
50455                  if(g.isEditor && !g.editing){
50456                     g.startEditing(r, c);
50457                     e.stopEvent();
50458                     return;
50459                 }
50460              break;
50461         };
50462         if(newCell){
50463             this.select(newCell[0], newCell[1]);
50464             e.stopEvent();
50465         }
50466     },
50467
50468     acceptsNav : function(row, col, cm){
50469         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50470     },
50471
50472     onEditorKey : function(field, e){
50473         
50474         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50475         ///Roo.log('onEditorKey' + k);
50476         
50477         if(k == e.TAB){
50478             if(e.shiftKey){
50479                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50480             }else{
50481                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50482             }
50483             e.stopEvent();
50484         }else if(k == e.ENTER && !e.ctrlKey){
50485             ed.completeEdit();
50486             e.stopEvent();
50487             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50488         }else if(k == e.ESC){
50489             ed.cancelEdit();
50490         }
50491         
50492         
50493         if(newCell){
50494             //Roo.log('next cell after edit');
50495             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50496         }
50497     }
50498 });/*
50499  * Based on:
50500  * Ext JS Library 1.1.1
50501  * Copyright(c) 2006-2007, Ext JS, LLC.
50502  *
50503  * Originally Released Under LGPL - original licence link has changed is not relivant.
50504  *
50505  * Fork - LGPL
50506  * <script type="text/javascript">
50507  */
50508  
50509 /**
50510  * @class Roo.grid.EditorGrid
50511  * @extends Roo.grid.Grid
50512  * Class for creating and editable grid.
50513  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50514  * The container MUST have some type of size defined for the grid to fill. The container will be 
50515  * automatically set to position relative if it isn't already.
50516  * @param {Object} dataSource The data model to bind to
50517  * @param {Object} colModel The column model with info about this grid's columns
50518  */
50519 Roo.grid.EditorGrid = function(container, config){
50520     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50521     this.getGridEl().addClass("xedit-grid");
50522
50523     if(!this.selModel){
50524         this.selModel = new Roo.grid.CellSelectionModel();
50525     }
50526
50527     this.activeEditor = null;
50528
50529         this.addEvents({
50530             /**
50531              * @event beforeedit
50532              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50533              * <ul style="padding:5px;padding-left:16px;">
50534              * <li>grid - This grid</li>
50535              * <li>record - The record being edited</li>
50536              * <li>field - The field name being edited</li>
50537              * <li>value - The value for the field being edited.</li>
50538              * <li>row - The grid row index</li>
50539              * <li>column - The grid column index</li>
50540              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50541              * </ul>
50542              * @param {Object} e An edit event (see above for description)
50543              */
50544             "beforeedit" : true,
50545             /**
50546              * @event afteredit
50547              * Fires after a cell is edited. <br />
50548              * <ul style="padding:5px;padding-left:16px;">
50549              * <li>grid - This grid</li>
50550              * <li>record - The record being edited</li>
50551              * <li>field - The field name being edited</li>
50552              * <li>value - The value being set</li>
50553              * <li>originalValue - The original value for the field, before the edit.</li>
50554              * <li>row - The grid row index</li>
50555              * <li>column - The grid column index</li>
50556              * </ul>
50557              * @param {Object} e An edit event (see above for description)
50558              */
50559             "afteredit" : true,
50560             /**
50561              * @event validateedit
50562              * Fires after a cell is edited, but before the value is set in the record. 
50563          * You can use this to modify the value being set in the field, Return false
50564              * to cancel the change. The edit event object has the following properties <br />
50565              * <ul style="padding:5px;padding-left:16px;">
50566          * <li>editor - This editor</li>
50567              * <li>grid - This grid</li>
50568              * <li>record - The record being edited</li>
50569              * <li>field - The field name being edited</li>
50570              * <li>value - The value being set</li>
50571              * <li>originalValue - The original value for the field, before the edit.</li>
50572              * <li>row - The grid row index</li>
50573              * <li>column - The grid column index</li>
50574              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50575              * </ul>
50576              * @param {Object} e An edit event (see above for description)
50577              */
50578             "validateedit" : true
50579         });
50580     this.on("bodyscroll", this.stopEditing,  this);
50581     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50582 };
50583
50584 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50585     /**
50586      * @cfg {Number} clicksToEdit
50587      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50588      */
50589     clicksToEdit: 2,
50590
50591     // private
50592     isEditor : true,
50593     // private
50594     trackMouseOver: false, // causes very odd FF errors
50595
50596     onCellDblClick : function(g, row, col){
50597         this.startEditing(row, col);
50598     },
50599
50600     onEditComplete : function(ed, value, startValue){
50601         this.editing = false;
50602         this.activeEditor = null;
50603         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50604         var r = ed.record;
50605         var field = this.colModel.getDataIndex(ed.col);
50606         var e = {
50607             grid: this,
50608             record: r,
50609             field: field,
50610             originalValue: startValue,
50611             value: value,
50612             row: ed.row,
50613             column: ed.col,
50614             cancel:false,
50615             editor: ed
50616         };
50617         if(String(value) !== String(startValue)){
50618             
50619             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50620                 r.set(field, e.value);
50621                 // if we are dealing with a combo box..
50622                 // then we also set the 'name' colum to be the displayField
50623                 if (ed.field.displayField && ed.field.name) {
50624                     r.set(ed.field.name, ed.field.el.dom.value);
50625                 }
50626                 
50627                 delete e.cancel; //?? why!!!
50628                 this.fireEvent("afteredit", e);
50629             }
50630         } else {
50631             this.fireEvent("afteredit", e); // always fire it!
50632         }
50633         this.view.focusCell(ed.row, ed.col);
50634     },
50635
50636     /**
50637      * Starts editing the specified for the specified row/column
50638      * @param {Number} rowIndex
50639      * @param {Number} colIndex
50640      */
50641     startEditing : function(row, col){
50642         this.stopEditing();
50643         if(this.colModel.isCellEditable(col, row)){
50644             this.view.ensureVisible(row, col, true);
50645             var r = this.dataSource.getAt(row);
50646             var field = this.colModel.getDataIndex(col);
50647             var e = {
50648                 grid: this,
50649                 record: r,
50650                 field: field,
50651                 value: r.data[field],
50652                 row: row,
50653                 column: col,
50654                 cancel:false
50655             };
50656             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50657                 this.editing = true;
50658                 var ed = this.colModel.getCellEditor(col, row);
50659                 
50660                 if (!ed) {
50661                     return;
50662                 }
50663                 if(!ed.rendered){
50664                     ed.render(ed.parentEl || document.body);
50665                 }
50666                 ed.field.reset();
50667                 (function(){ // complex but required for focus issues in safari, ie and opera
50668                     ed.row = row;
50669                     ed.col = col;
50670                     ed.record = r;
50671                     ed.on("complete", this.onEditComplete, this, {single: true});
50672                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50673                     this.activeEditor = ed;
50674                     var v = r.data[field];
50675                     ed.startEdit(this.view.getCell(row, col), v);
50676                     // combo's with 'displayField and name set
50677                     if (ed.field.displayField && ed.field.name) {
50678                         ed.field.el.dom.value = r.data[ed.field.name];
50679                     }
50680                     
50681                     
50682                 }).defer(50, this);
50683             }
50684         }
50685     },
50686         
50687     /**
50688      * Stops any active editing
50689      */
50690     stopEditing : function(){
50691         if(this.activeEditor){
50692             this.activeEditor.completeEdit();
50693         }
50694         this.activeEditor = null;
50695     }
50696 });/*
50697  * Based on:
50698  * Ext JS Library 1.1.1
50699  * Copyright(c) 2006-2007, Ext JS, LLC.
50700  *
50701  * Originally Released Under LGPL - original licence link has changed is not relivant.
50702  *
50703  * Fork - LGPL
50704  * <script type="text/javascript">
50705  */
50706
50707 // private - not really -- you end up using it !
50708 // This is a support class used internally by the Grid components
50709
50710 /**
50711  * @class Roo.grid.GridEditor
50712  * @extends Roo.Editor
50713  * Class for creating and editable grid elements.
50714  * @param {Object} config any settings (must include field)
50715  */
50716 Roo.grid.GridEditor = function(field, config){
50717     if (!config && field.field) {
50718         config = field;
50719         field = Roo.factory(config.field, Roo.form);
50720     }
50721     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50722     field.monitorTab = false;
50723 };
50724
50725 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50726     
50727     /**
50728      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50729      */
50730     
50731     alignment: "tl-tl",
50732     autoSize: "width",
50733     hideEl : false,
50734     cls: "x-small-editor x-grid-editor",
50735     shim:false,
50736     shadow:"frame"
50737 });/*
50738  * Based on:
50739  * Ext JS Library 1.1.1
50740  * Copyright(c) 2006-2007, Ext JS, LLC.
50741  *
50742  * Originally Released Under LGPL - original licence link has changed is not relivant.
50743  *
50744  * Fork - LGPL
50745  * <script type="text/javascript">
50746  */
50747   
50748
50749   
50750 Roo.grid.PropertyRecord = Roo.data.Record.create([
50751     {name:'name',type:'string'},  'value'
50752 ]);
50753
50754
50755 Roo.grid.PropertyStore = function(grid, source){
50756     this.grid = grid;
50757     this.store = new Roo.data.Store({
50758         recordType : Roo.grid.PropertyRecord
50759     });
50760     this.store.on('update', this.onUpdate,  this);
50761     if(source){
50762         this.setSource(source);
50763     }
50764     Roo.grid.PropertyStore.superclass.constructor.call(this);
50765 };
50766
50767
50768
50769 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50770     setSource : function(o){
50771         this.source = o;
50772         this.store.removeAll();
50773         var data = [];
50774         for(var k in o){
50775             if(this.isEditableValue(o[k])){
50776                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50777             }
50778         }
50779         this.store.loadRecords({records: data}, {}, true);
50780     },
50781
50782     onUpdate : function(ds, record, type){
50783         if(type == Roo.data.Record.EDIT){
50784             var v = record.data['value'];
50785             var oldValue = record.modified['value'];
50786             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50787                 this.source[record.id] = v;
50788                 record.commit();
50789                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50790             }else{
50791                 record.reject();
50792             }
50793         }
50794     },
50795
50796     getProperty : function(row){
50797        return this.store.getAt(row);
50798     },
50799
50800     isEditableValue: function(val){
50801         if(val && val instanceof Date){
50802             return true;
50803         }else if(typeof val == 'object' || typeof val == 'function'){
50804             return false;
50805         }
50806         return true;
50807     },
50808
50809     setValue : function(prop, value){
50810         this.source[prop] = value;
50811         this.store.getById(prop).set('value', value);
50812     },
50813
50814     getSource : function(){
50815         return this.source;
50816     }
50817 });
50818
50819 Roo.grid.PropertyColumnModel = function(grid, store){
50820     this.grid = grid;
50821     var g = Roo.grid;
50822     g.PropertyColumnModel.superclass.constructor.call(this, [
50823         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50824         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50825     ]);
50826     this.store = store;
50827     this.bselect = Roo.DomHelper.append(document.body, {
50828         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50829             {tag: 'option', value: 'true', html: 'true'},
50830             {tag: 'option', value: 'false', html: 'false'}
50831         ]
50832     });
50833     Roo.id(this.bselect);
50834     var f = Roo.form;
50835     this.editors = {
50836         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50837         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50838         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50839         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50840         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50841     };
50842     this.renderCellDelegate = this.renderCell.createDelegate(this);
50843     this.renderPropDelegate = this.renderProp.createDelegate(this);
50844 };
50845
50846 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50847     
50848     
50849     nameText : 'Name',
50850     valueText : 'Value',
50851     
50852     dateFormat : 'm/j/Y',
50853     
50854     
50855     renderDate : function(dateVal){
50856         return dateVal.dateFormat(this.dateFormat);
50857     },
50858
50859     renderBool : function(bVal){
50860         return bVal ? 'true' : 'false';
50861     },
50862
50863     isCellEditable : function(colIndex, rowIndex){
50864         return colIndex == 1;
50865     },
50866
50867     getRenderer : function(col){
50868         return col == 1 ?
50869             this.renderCellDelegate : this.renderPropDelegate;
50870     },
50871
50872     renderProp : function(v){
50873         return this.getPropertyName(v);
50874     },
50875
50876     renderCell : function(val){
50877         var rv = val;
50878         if(val instanceof Date){
50879             rv = this.renderDate(val);
50880         }else if(typeof val == 'boolean'){
50881             rv = this.renderBool(val);
50882         }
50883         return Roo.util.Format.htmlEncode(rv);
50884     },
50885
50886     getPropertyName : function(name){
50887         var pn = this.grid.propertyNames;
50888         return pn && pn[name] ? pn[name] : name;
50889     },
50890
50891     getCellEditor : function(colIndex, rowIndex){
50892         var p = this.store.getProperty(rowIndex);
50893         var n = p.data['name'], val = p.data['value'];
50894         
50895         if(typeof(this.grid.customEditors[n]) == 'string'){
50896             return this.editors[this.grid.customEditors[n]];
50897         }
50898         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50899             return this.grid.customEditors[n];
50900         }
50901         if(val instanceof Date){
50902             return this.editors['date'];
50903         }else if(typeof val == 'number'){
50904             return this.editors['number'];
50905         }else if(typeof val == 'boolean'){
50906             return this.editors['boolean'];
50907         }else{
50908             return this.editors['string'];
50909         }
50910     }
50911 });
50912
50913 /**
50914  * @class Roo.grid.PropertyGrid
50915  * @extends Roo.grid.EditorGrid
50916  * This class represents the  interface of a component based property grid control.
50917  * <br><br>Usage:<pre><code>
50918  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50919       
50920  });
50921  // set any options
50922  grid.render();
50923  * </code></pre>
50924   
50925  * @constructor
50926  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50927  * The container MUST have some type of size defined for the grid to fill. The container will be
50928  * automatically set to position relative if it isn't already.
50929  * @param {Object} config A config object that sets properties on this grid.
50930  */
50931 Roo.grid.PropertyGrid = function(container, config){
50932     config = config || {};
50933     var store = new Roo.grid.PropertyStore(this);
50934     this.store = store;
50935     var cm = new Roo.grid.PropertyColumnModel(this, store);
50936     store.store.sort('name', 'ASC');
50937     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50938         ds: store.store,
50939         cm: cm,
50940         enableColLock:false,
50941         enableColumnMove:false,
50942         stripeRows:false,
50943         trackMouseOver: false,
50944         clicksToEdit:1
50945     }, config));
50946     this.getGridEl().addClass('x-props-grid');
50947     this.lastEditRow = null;
50948     this.on('columnresize', this.onColumnResize, this);
50949     this.addEvents({
50950          /**
50951              * @event beforepropertychange
50952              * Fires before a property changes (return false to stop?)
50953              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50954              * @param {String} id Record Id
50955              * @param {String} newval New Value
50956          * @param {String} oldval Old Value
50957              */
50958         "beforepropertychange": true,
50959         /**
50960              * @event propertychange
50961              * Fires after a property changes
50962              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50963              * @param {String} id Record Id
50964              * @param {String} newval New Value
50965          * @param {String} oldval Old Value
50966              */
50967         "propertychange": true
50968     });
50969     this.customEditors = this.customEditors || {};
50970 };
50971 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50972     
50973      /**
50974      * @cfg {Object} customEditors map of colnames=> custom editors.
50975      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50976      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50977      * false disables editing of the field.
50978          */
50979     
50980       /**
50981      * @cfg {Object} propertyNames map of property Names to their displayed value
50982          */
50983     
50984     render : function(){
50985         Roo.grid.PropertyGrid.superclass.render.call(this);
50986         this.autoSize.defer(100, this);
50987     },
50988
50989     autoSize : function(){
50990         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50991         if(this.view){
50992             this.view.fitColumns();
50993         }
50994     },
50995
50996     onColumnResize : function(){
50997         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50998         this.autoSize();
50999     },
51000     /**
51001      * Sets the data for the Grid
51002      * accepts a Key => Value object of all the elements avaiable.
51003      * @param {Object} data  to appear in grid.
51004      */
51005     setSource : function(source){
51006         this.store.setSource(source);
51007         //this.autoSize();
51008     },
51009     /**
51010      * Gets all the data from the grid.
51011      * @return {Object} data  data stored in grid
51012      */
51013     getSource : function(){
51014         return this.store.getSource();
51015     }
51016 });/*
51017  * Based on:
51018  * Ext JS Library 1.1.1
51019  * Copyright(c) 2006-2007, Ext JS, LLC.
51020  *
51021  * Originally Released Under LGPL - original licence link has changed is not relivant.
51022  *
51023  * Fork - LGPL
51024  * <script type="text/javascript">
51025  */
51026  
51027 /**
51028  * @class Roo.LoadMask
51029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51032  * element's UpdateManager load indicator and will be destroyed after the initial load.
51033  * @constructor
51034  * Create a new LoadMask
51035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51036  * @param {Object} config The config object
51037  */
51038 Roo.LoadMask = function(el, config){
51039     this.el = Roo.get(el);
51040     Roo.apply(this, config);
51041     if(this.store){
51042         this.store.on('beforeload', this.onBeforeLoad, this);
51043         this.store.on('load', this.onLoad, this);
51044         this.store.on('loadexception', this.onLoad, this);
51045         this.removeMask = false;
51046     }else{
51047         var um = this.el.getUpdateManager();
51048         um.showLoadIndicator = false; // disable the default indicator
51049         um.on('beforeupdate', this.onBeforeLoad, this);
51050         um.on('update', this.onLoad, this);
51051         um.on('failure', this.onLoad, this);
51052         this.removeMask = true;
51053     }
51054 };
51055
51056 Roo.LoadMask.prototype = {
51057     /**
51058      * @cfg {Boolean} removeMask
51059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51061      */
51062     /**
51063      * @cfg {String} msg
51064      * The text to display in a centered loading message box (defaults to 'Loading...')
51065      */
51066     msg : 'Loading...',
51067     /**
51068      * @cfg {String} msgCls
51069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51070      */
51071     msgCls : 'x-mask-loading',
51072
51073     /**
51074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51075      * @type Boolean
51076      */
51077     disabled: false,
51078
51079     /**
51080      * Disables the mask to prevent it from being displayed
51081      */
51082     disable : function(){
51083        this.disabled = true;
51084     },
51085
51086     /**
51087      * Enables the mask so that it can be displayed
51088      */
51089     enable : function(){
51090         this.disabled = false;
51091     },
51092
51093     // private
51094     onLoad : function(){
51095         this.el.unmask(this.removeMask);
51096     },
51097
51098     // private
51099     onBeforeLoad : function(){
51100         if(!this.disabled){
51101             this.el.mask(this.msg, this.msgCls);
51102         }
51103     },
51104
51105     // private
51106     destroy : function(){
51107         if(this.store){
51108             this.store.un('beforeload', this.onBeforeLoad, this);
51109             this.store.un('load', this.onLoad, this);
51110             this.store.un('loadexception', this.onLoad, this);
51111         }else{
51112             var um = this.el.getUpdateManager();
51113             um.un('beforeupdate', this.onBeforeLoad, this);
51114             um.un('update', this.onLoad, this);
51115             um.un('failure', this.onLoad, this);
51116         }
51117     }
51118 };/*
51119  * Based on:
51120  * Ext JS Library 1.1.1
51121  * Copyright(c) 2006-2007, Ext JS, LLC.
51122  *
51123  * Originally Released Under LGPL - original licence link has changed is not relivant.
51124  *
51125  * Fork - LGPL
51126  * <script type="text/javascript">
51127  */
51128 Roo.XTemplate = function(){
51129     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51130     var s = this.html;
51131
51132     s = ['<tpl>', s, '</tpl>'].join('');
51133
51134     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51135
51136     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51137     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51138     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51139     var m, id = 0;
51140     var tpls = [];
51141
51142     while(m = s.match(re)){
51143        var m2 = m[0].match(nameRe);
51144        var m3 = m[0].match(ifRe);
51145        var m4 = m[0].match(execRe);
51146        var exp = null, fn = null, exec = null;
51147        var name = m2 && m2[1] ? m2[1] : '';
51148        if(m3){
51149            exp = m3 && m3[1] ? m3[1] : null;
51150            if(exp){
51151                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51152            }
51153        }
51154        if(m4){
51155            exp = m4 && m4[1] ? m4[1] : null;
51156            if(exp){
51157                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51158            }
51159        }
51160        if(name){
51161            switch(name){
51162                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51163                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51164                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51165            }
51166        }
51167        tpls.push({
51168             id: id,
51169             target: name,
51170             exec: exec,
51171             test: fn,
51172             body: m[1]||''
51173         });
51174        s = s.replace(m[0], '{xtpl'+ id + '}');
51175        ++id;
51176     }
51177     for(var i = tpls.length-1; i >= 0; --i){
51178         this.compileTpl(tpls[i]);
51179     }
51180     this.master = tpls[tpls.length-1];
51181     this.tpls = tpls;
51182 };
51183 Roo.extend(Roo.XTemplate, Roo.Template, {
51184
51185     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51186
51187     applySubTemplate : function(id, values, parent){
51188         var t = this.tpls[id];
51189         if(t.test && !t.test.call(this, values, parent)){
51190             return '';
51191         }
51192         if(t.exec && t.exec.call(this, values, parent)){
51193             return '';
51194         }
51195         var vs = t.target ? t.target.call(this, values, parent) : values;
51196         parent = t.target ? values : parent;
51197         if(t.target && vs instanceof Array){
51198             var buf = [];
51199             for(var i = 0, len = vs.length; i < len; i++){
51200                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51201             }
51202             return buf.join('');
51203         }
51204         return t.compiled.call(this, vs, parent);
51205     },
51206
51207     compileTpl : function(tpl){
51208         var fm = Roo.util.Format;
51209         var useF = this.disableFormats !== true;
51210         var sep = Roo.isGecko ? "+" : ",";
51211         var fn = function(m, name, format, args){
51212             if(name.substr(0, 4) == 'xtpl'){
51213                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51214             }
51215             var v;
51216             if(name.indexOf('.') != -1){
51217                 v = name;
51218             }else{
51219                 v = "values['" + name + "']";
51220             }
51221             if(format && useF){
51222                 args = args ? ',' + args : "";
51223                 if(format.substr(0, 5) != "this."){
51224                     format = "fm." + format + '(';
51225                 }else{
51226                     format = 'this.call("'+ format.substr(5) + '", ';
51227                     args = ", values";
51228                 }
51229             }else{
51230                 args= ''; format = "("+v+" === undefined ? '' : ";
51231             }
51232             return "'"+ sep + format + v + args + ")"+sep+"'";
51233         };
51234         var body;
51235         // branched to use + in gecko and [].join() in others
51236         if(Roo.isGecko){
51237             body = "tpl.compiled = function(values, parent){ return '" +
51238                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51239                     "';};";
51240         }else{
51241             body = ["tpl.compiled = function(values, parent){ return ['"];
51242             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51243             body.push("'].join('');};");
51244             body = body.join('');
51245         }
51246         /** eval:var:zzzzzzz */
51247         eval(body);
51248         return this;
51249     },
51250
51251     applyTemplate : function(values){
51252         return this.master.compiled.call(this, values, {});
51253         var s = this.subs;
51254     },
51255
51256     apply : function(){
51257         return this.applyTemplate.apply(this, arguments);
51258     },
51259
51260     compile : function(){return this;}
51261 });
51262
51263 Roo.XTemplate.from = function(el){
51264     el = Roo.getDom(el);
51265     return new Roo.XTemplate(el.value || el.innerHTML);
51266 };/*
51267  * Original code for Roojs - LGPL
51268  * <script type="text/javascript">
51269  */
51270  
51271 /**
51272  * @class Roo.XComponent
51273  * A delayed Element creator...
51274  * Or a way to group chunks of interface together.
51275  * 
51276  * Mypart.xyx = new Roo.XComponent({
51277
51278     parent : 'Mypart.xyz', // empty == document.element.!!
51279     order : '001',
51280     name : 'xxxx'
51281     region : 'xxxx'
51282     disabled : function() {} 
51283      
51284     tree : function() { // return an tree of xtype declared components
51285         var MODULE = this;
51286         return 
51287         {
51288             xtype : 'NestedLayoutPanel',
51289             // technicall
51290         }
51291      ]
51292  *})
51293  *
51294  *
51295  * It can be used to build a big heiracy, with parent etc.
51296  * or you can just use this to render a single compoent to a dom element
51297  * MYPART.render(Roo.Element | String(id) | dom_element )
51298  * 
51299  * @extends Roo.util.Observable
51300  * @constructor
51301  * @param cfg {Object} configuration of component
51302  * 
51303  */
51304 Roo.XComponent = function(cfg) {
51305     Roo.apply(this, cfg);
51306     this.addEvents({ 
51307         /**
51308              * @event built
51309              * Fires when this the componnt is built
51310              * @param {Roo.XComponent} c the component
51311              */
51312         'built' : true,
51313         /**
51314              * @event buildcomplete
51315              * Fires on the top level element when all elements have been built
51316              * @param {Roo.XComponent} c the top level component.
51317          */
51318         'buildcomplete' : true
51319         
51320     });
51321     this.region = this.region || 'center'; // default..
51322     Roo.XComponent.register(this);
51323     this.modules = false;
51324     this.el = false; // where the layout goes..
51325     
51326     
51327 }
51328 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51329     /**
51330      * @property el
51331      * The created element (with Roo.factory())
51332      * @type {Roo.Layout}
51333      */
51334     el  : false,
51335     
51336     /**
51337      * @property el
51338      * for BC  - use el in new code
51339      * @type {Roo.Layout}
51340      */
51341     panel : false,
51342     
51343     /**
51344      * @property layout
51345      * for BC  - use el in new code
51346      * @type {Roo.Layout}
51347      */
51348     layout : false,
51349     
51350      /**
51351      * @cfg {Function|boolean} disabled
51352      * If this module is disabled by some rule, return true from the funtion
51353      */
51354     disabled : false,
51355     
51356     /**
51357      * @cfg {String} parent 
51358      * Name of parent element which it get xtype added to..
51359      */
51360     parent: false,
51361     
51362     /**
51363      * @cfg {String} order
51364      * Used to set the order in which elements are created (usefull for multiple tabs)
51365      */
51366     
51367     order : false,
51368     /**
51369      * @cfg {String} name
51370      * String to display while loading.
51371      */
51372     name : false,
51373     /**
51374      * @cfg {String} region
51375      * Region to render component to (defaults to center)
51376      */
51377     region : 'center',
51378     
51379     /**
51380      * @cfg {Array} items
51381      * A single item array - the first element is the root of the tree..
51382      * It's done this way to stay compatible with the Xtype system...
51383      */
51384     items : false,
51385     
51386     
51387      /**
51388      * render
51389      * render element to dom or tree
51390      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51391      */
51392     
51393     render : function(el)
51394     {
51395         
51396         el = el || false;
51397         
51398         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
51399             // if parent is a '#.....' string, then let's use that..
51400             var ename = this.parent.substr(1)
51401             this.parent = false;
51402             el = Roo.get(ename);
51403             if (!el) {
51404                 Roo.log("Warning - element can not be found :#" + ename );
51405                 return;
51406             }
51407         }
51408         if (!this.parent) {
51409             
51410             el = el ? Roo.get(el) : false;
51411             
51412             // it's a top level one..
51413             this.parent =  {
51414                 el : new Roo.BorderLayout(el || document.body, {
51415                 
51416                      center: {
51417                          titlebar: false,
51418                          autoScroll:false,
51419                          closeOnTab: true,
51420                          tabPosition: 'top',
51421                           //resizeTabs: true,
51422                          alwaysShowTabs: el ? false :  true,
51423                          hideTabs: el ? true :  false,
51424                          minTabWidth: 140
51425                      }
51426                  })
51427             }
51428         }
51429         
51430         
51431             
51432         var tree = this.tree();
51433         tree.region = tree.region || this.region;
51434         this.el = this.parent.el.addxtype(tree);
51435         this.fireEvent('built', this);
51436         
51437         this.panel = this.el;
51438         this.layout = this.panel.layout;    
51439          
51440     }
51441     
51442 });
51443
51444 Roo.apply(Roo.XComponent, {
51445     
51446     /**
51447      * @property  buildCompleted
51448      * True when the builder has completed building the interface.
51449      * @type Boolean
51450      */
51451     buildCompleted : false,
51452      
51453     /**
51454      * @property  topModule
51455      * the upper most module - uses document.element as it's constructor.
51456      * @type Object
51457      */
51458      
51459     topModule  : false,
51460       
51461     /**
51462      * @property  modules
51463      * array of modules to be created by registration system.
51464      * @type {Array} of Roo.XComponent
51465      */
51466     
51467     modules : [],
51468     /**
51469      * @property  elmodules
51470      * array of modules to be created by which use #ID 
51471      * @type {Array} of Roo.XComponent
51472      */
51473      
51474     elmodules : [],
51475
51476     
51477     /**
51478      * Register components to be built later.
51479      *
51480      * This solves the following issues
51481      * - Building is not done on page load, but after an authentication process has occured.
51482      * - Interface elements are registered on page load
51483      * - Parent Interface elements may not be loaded before child, so this handles that..
51484      * 
51485      *
51486      * example:
51487      * 
51488      * MyApp.register({
51489           order : '000001',
51490           module : 'Pman.Tab.projectMgr',
51491           region : 'center',
51492           parent : 'Pman.layout',
51493           disabled : false,  // or use a function..
51494         })
51495      
51496      * * @param {Object} details about module
51497      */
51498     register : function(obj) {
51499         this.modules.push(obj);
51500          
51501     },
51502     /**
51503      * convert a string to an object..
51504      * eg. 'AAA.BBB' -> finds AAA.BBB
51505
51506      */
51507     
51508     toObject : function(str)
51509     {
51510         if (!str || typeof(str) == 'object') {
51511             return str;
51512         }
51513         if (str[0]=='#') {
51514             return str;
51515         }
51516
51517         var ar = str.split('.');
51518         var rt, o;
51519         rt = ar.shift();
51520             /** eval:var:o */
51521         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51522         if (o === false) {
51523             throw "Module not found : " + str;
51524         }
51525         Roo.each(ar, function(e) {
51526             if (typeof(o[e]) == 'undefined') {
51527                 throw "Module not found : " + str;
51528             }
51529             o = o[e];
51530         });
51531         return o;
51532         
51533     },
51534     
51535     
51536     /**
51537      * move modules into their correct place in the tree..
51538      * 
51539      */
51540     preBuild : function ()
51541     {
51542         
51543         Roo.each(this.modules , function (obj)
51544         {
51545             obj.parent = this.toObject(obj.parent);
51546             
51547             if (!obj.parent) {
51548                 this.topModule = obj;
51549                 return;
51550             }
51551             if (typeof(obj.parent) == 'string') {
51552                 this.elmodules.push(obj);
51553                 return;
51554             }
51555             
51556             if (!obj.parent.modules) {
51557                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51558                     function(o) { return o.order + '' }
51559                 );
51560             }
51561             
51562             obj.parent.modules.add(obj);
51563         }, this);
51564     },
51565     
51566      /**
51567      * make a list of modules to build.
51568      * @return {Array} list of modules. 
51569      */ 
51570     
51571     buildOrder : function()
51572     {
51573         var _this = this;
51574         var cmp = function(a,b) {   
51575             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51576         };
51577         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51578             throw "No top level modules to build";
51579         }
51580         
51581         // make a flat list in order of modules to build.
51582         var mods = this.topModule ? [ this.topModule ] : [];
51583         Roo.each(this.elmodules,function(e) { mods.push(e) });
51584
51585         
51586         // add modules to their parents..
51587         var addMod = function(m) {
51588            // Roo.debug && Roo.log(m.modKey);
51589             
51590             mods.push(m);
51591             if (m.modules) {
51592                 m.modules.keySort('ASC',  cmp );
51593                 m.modules.each(addMod);
51594             }
51595             // not sure if this is used any more..
51596             if (m.finalize) {
51597                 m.finalize.name = m.name + " (clean up) ";
51598                 mods.push(m.finalize);
51599             }
51600             
51601         }
51602         if (this.topModule) { 
51603             this.topModule.modules.keySort('ASC',  cmp );
51604             this.topModule.modules.each(addMod);
51605         }
51606         return mods;
51607     },
51608     
51609      /**
51610      * Build the registered modules.
51611      * @param {Object} parent element.
51612      * @param {Function} optional method to call after module has been added.
51613      * 
51614      */ 
51615    
51616     build : function() 
51617     {
51618         
51619         this.preBuild();
51620         var mods = this.buildOrder();
51621       
51622         //this.allmods = mods;
51623         //Roo.debug && Roo.log(mods);
51624         //return;
51625         if (!mods.length) { // should not happen
51626             throw "NO modules!!!";
51627         }
51628         
51629         
51630         
51631         // flash it up as modal - so we store the mask!?
51632         Roo.MessageBox.show({ title: 'loading' });
51633         Roo.MessageBox.show({
51634            title: "Please wait...",
51635            msg: "Building Interface...",
51636            width:450,
51637            progress:true,
51638            closable:false,
51639            modal: false
51640           
51641         });
51642         var total = mods.length;
51643         
51644         var _this = this;
51645         var progressRun = function() {
51646             if (!mods.length) {
51647                 Roo.debug && Roo.log('hide?');
51648                 Roo.MessageBox.hide();
51649                 if (_this.topModule) { 
51650                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51651                 }
51652                 // THE END...
51653                 return false;   
51654             }
51655             
51656             var m = mods.shift();
51657             
51658             
51659             Roo.debug && Roo.log(m);
51660             // not sure if this is supported any more.. - modules that are are just function
51661             if (typeof(m) == 'function') { 
51662                 m.call(this);
51663                 return progressRun.defer(10, _this);
51664             } 
51665             
51666             
51667             
51668             Roo.MessageBox.updateProgress(
51669                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51670                     " of " + total + 
51671                     (m.name ? (' - ' + m.name) : '')
51672                     );
51673             
51674          
51675             // is the module disabled?
51676             var disabled = (typeof(m.disabled) == 'function') ?
51677                 m.disabled.call(m.module.disabled) : m.disabled;    
51678             
51679             
51680             if (disabled) {
51681                 return progressRun(); // we do not update the display!
51682             }
51683             
51684             // now build 
51685             
51686             m.render();
51687             // it's 10 on top level, and 1 on others??? why...
51688             return progressRun.defer(10, _this);
51689              
51690         }
51691         progressRun.defer(1, _this);
51692      
51693         
51694         
51695     }
51696     
51697      
51698    
51699     
51700     
51701 });
51702  //<script type="text/javascript">
51703
51704
51705 /**
51706  * @class Roo.Login
51707  * @extends Roo.LayoutDialog
51708  * A generic Login Dialog..... - only one needed in theory!?!?
51709  *
51710  * Fires XComponent builder on success...
51711  * 
51712  * Sends 
51713  *    username,password, lang = for login actions.
51714  *    check = 1 for periodic checking that sesion is valid.
51715  *    passwordRequest = email request password
51716  *    logout = 1 = to logout
51717  * 
51718  * Affects: (this id="????" elements)
51719  *   loading  (removed) (used to indicate application is loading)
51720  *   loading-mask (hides) (used to hide application when it's building loading)
51721  *   
51722  * 
51723  * Usage: 
51724  *    
51725  * 
51726  * Myapp.login = Roo.Login({
51727      url: xxxx,
51728    
51729      realm : 'Myapp', 
51730      
51731      
51732      method : 'POST',
51733      
51734      
51735      * 
51736  })
51737  * 
51738  * 
51739  * 
51740  **/
51741  
51742 Roo.Login = function(cfg)
51743 {
51744     this.addEvents({
51745         'refreshed' : true
51746     });
51747     
51748     Roo.apply(this,cfg);
51749     
51750     Roo.onReady(function() {
51751         this.onLoad();
51752     }, this);
51753     // call parent..
51754     
51755    
51756     Roo.Login.superclass.constructor.call(this, this);
51757     //this.addxtype(this.items[0]);
51758     
51759     
51760 }
51761
51762
51763 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51764     
51765     /**
51766      * @cfg {String} method
51767      * Method used to query for login details.
51768      */
51769     
51770     method : 'POST',
51771     /**
51772      * @cfg {String} url
51773      * URL to query login data. - eg. baseURL + '/Login.php'
51774      */
51775     url : '',
51776     
51777     /**
51778      * @property user
51779      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51780      * @type {Object} 
51781      */
51782     user : false,
51783     /**
51784      * @property checkFails
51785      * Number of times we have attempted to get authentication check, and failed.
51786      * @type {Number} 
51787      */
51788     checkFails : 0,
51789       /**
51790      * @property intervalID
51791      * The window interval that does the constant login checking.
51792      * @type {Number} 
51793      */
51794     intervalID : 0,
51795     
51796     
51797     onLoad : function() // called on page load...
51798     {
51799         // load 
51800          
51801         if (Roo.get('loading')) { // clear any loading indicator..
51802             Roo.get('loading').remove();
51803         }
51804         
51805         //this.switchLang('en'); // set the language to english..
51806        
51807         this.check({
51808             success:  function(response, opts)  {  // check successfull...
51809             
51810                 var res = this.processResponse(response);
51811                 this.checkFails =0;
51812                 if (!res.success) { // error!
51813                     this.checkFails = 5;
51814                     //console.log('call failure');
51815                     return this.failure(response,opts);
51816                 }
51817                 
51818                 if (!res.data.id) { // id=0 == login failure.
51819                     return this.show();
51820                 }
51821                 
51822                               
51823                         //console.log(success);
51824                 this.fillAuth(res.data);   
51825                 this.checkFails =0;
51826                 Roo.XComponent.build();
51827             },
51828             failure : this.show
51829         });
51830         
51831     }, 
51832     
51833     
51834     check: function(cfg) // called every so often to refresh cookie etc..
51835     {
51836         if (cfg.again) { // could be undefined..
51837             this.checkFails++;
51838         } else {
51839             this.checkFails = 0;
51840         }
51841         var _this = this;
51842         if (this.sending) {
51843             if ( this.checkFails > 4) {
51844                 Roo.MessageBox.alert("Error",  
51845                     "Error getting authentication status. - try reloading, or wait a while", function() {
51846                         _this.sending = false;
51847                     }); 
51848                 return;
51849             }
51850             cfg.again = true;
51851             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51852             return;
51853         }
51854         this.sending = true;
51855         
51856         Roo.Ajax.request({  
51857             url: this.url,
51858             params: {
51859                 getAuthUser: true
51860             },  
51861             method: this.method,
51862             success:  cfg.success || this.success,
51863             failure : cfg.failure || this.failure,
51864             scope : this,
51865             callCfg : cfg
51866               
51867         });  
51868     }, 
51869     
51870     
51871     logout: function()
51872     {
51873         window.onbeforeunload = function() { }; // false does not work for IE..
51874         this.user = false;
51875         var _this = this;
51876         
51877         Roo.Ajax.request({  
51878             url: this.url,
51879             params: {
51880                 logout: 1
51881             },  
51882             method: 'GET',
51883             failure : function() {
51884                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51885                     document.location = document.location.toString() + '?ts=' + Math.random();
51886                 });
51887                 
51888             },
51889             success : function() {
51890                 _this.user = false;
51891                 this.checkFails =0;
51892                 // fixme..
51893                 document.location = document.location.toString() + '?ts=' + Math.random();
51894             }
51895               
51896               
51897         }); 
51898     },
51899     
51900     processResponse : function (response)
51901     {
51902         var res = '';
51903         try {
51904             res = Roo.decode(response.responseText);
51905             // oops...
51906             if (typeof(res) != 'object') {
51907                 res = { success : false, errorMsg : res, errors : true };
51908             }
51909             if (typeof(res.success) == 'undefined') {
51910                 res.success = false;
51911             }
51912             
51913         } catch(e) {
51914             res = { success : false,  errorMsg : response.responseText, errors : true };
51915         }
51916         return res;
51917     },
51918     
51919     success : function(response, opts)  // check successfull...
51920     {  
51921         this.sending = false;
51922         var res = this.processResponse(response);
51923         if (!res.success) {
51924             return this.failure(response, opts);
51925         }
51926         if (!res.data || !res.data.id) {
51927             return this.failure(response,opts);
51928         }
51929         //console.log(res);
51930         this.fillAuth(res.data);
51931         
51932         this.checkFails =0;
51933         
51934     },
51935     
51936     
51937     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51938     {
51939         this.authUser = -1;
51940         this.sending = false;
51941         var res = this.processResponse(response);
51942         //console.log(res);
51943         if ( this.checkFails > 2) {
51944         
51945             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51946                 "Error getting authentication status. - try reloading"); 
51947             return;
51948         }
51949         opts.callCfg.again = true;
51950         this.check.defer(1000, this, [ opts.callCfg ]);
51951         return;  
51952     },
51953     
51954     
51955     
51956     fillAuth: function(au) {
51957         this.startAuthCheck();
51958         this.authUserId = au.id;
51959         this.authUser = au;
51960         this.lastChecked = new Date();
51961         this.fireEvent('refreshed', au);
51962         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51963         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51964         au.lang = au.lang || 'en';
51965         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51966         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51967         this.switchLang(au.lang );
51968         
51969      
51970         // open system... - -on setyp..
51971         if (this.authUserId  < 0) {
51972             Roo.MessageBox.alert("Warning", 
51973                 "This is an open system - please set up a admin user with a password.");  
51974         }
51975          
51976         //Pman.onload(); // which should do nothing if it's a re-auth result...
51977         
51978              
51979     },
51980     
51981     startAuthCheck : function() // starter for timeout checking..
51982     {
51983         if (this.intervalID) { // timer already in place...
51984             return false;
51985         }
51986         var _this = this;
51987         this.intervalID =  window.setInterval(function() {
51988               _this.check(false);
51989             }, 120000); // every 120 secs = 2mins..
51990         
51991         
51992     },
51993          
51994     
51995     switchLang : function (lang) 
51996     {
51997         _T = typeof(_T) == 'undefined' ? false : _T;
51998           if (!_T || !lang.length) {
51999             return;
52000         }
52001         
52002         if (!_T && lang != 'en') {
52003             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52004             return;
52005         }
52006         
52007         if (typeof(_T.en) == 'undefined') {
52008             _T.en = {};
52009             Roo.apply(_T.en, _T);
52010         }
52011         
52012         if (typeof(_T[lang]) == 'undefined') {
52013             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52014             return;
52015         }
52016         
52017         
52018         Roo.apply(_T, _T[lang]);
52019         // just need to set the text values for everything...
52020         var _this = this;
52021         /* this will not work ...
52022         if (this.form) { 
52023             
52024                
52025             function formLabel(name, val) {
52026                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52027             }
52028             
52029             formLabel('password', "Password"+':');
52030             formLabel('username', "Email Address"+':');
52031             formLabel('lang', "Language"+':');
52032             this.dialog.setTitle("Login");
52033             this.dialog.buttons[0].setText("Forgot Password");
52034             this.dialog.buttons[1].setText("Login");
52035         }
52036         */
52037         
52038         
52039     },
52040     
52041     
52042     title: "Login",
52043     modal: true,
52044     width:  350,
52045     //height: 230,
52046     height: 180,
52047     shadow: true,
52048     minWidth:200,
52049     minHeight:180,
52050     //proxyDrag: true,
52051     closable: false,
52052     draggable: false,
52053     collapsible: false,
52054     resizable: false,
52055     center: {  // needed??
52056         autoScroll:false,
52057         titlebar: false,
52058        // tabPosition: 'top',
52059         hideTabs: true,
52060         closeOnTab: true,
52061         alwaysShowTabs: false
52062     } ,
52063     listeners : {
52064         
52065         show  : function(dlg)
52066         {
52067             //console.log(this);
52068             this.form = this.layout.getRegion('center').activePanel.form;
52069             this.form.dialog = dlg;
52070             this.buttons[0].form = this.form;
52071             this.buttons[0].dialog = dlg;
52072             this.buttons[1].form = this.form;
52073             this.buttons[1].dialog = dlg;
52074            
52075            //this.resizeToLogo.defer(1000,this);
52076             // this is all related to resizing for logos..
52077             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52078            //// if (!sz) {
52079              //   this.resizeToLogo.defer(1000,this);
52080              //   return;
52081            // }
52082             //var w = Ext.lib.Dom.getViewWidth() - 100;
52083             //var h = Ext.lib.Dom.getViewHeight() - 100;
52084             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52085             //this.center();
52086             if (this.disabled) {
52087                 this.hide();
52088                 return;
52089             }
52090             
52091             if (this.user.id < 0) { // used for inital setup situations.
52092                 return;
52093             }
52094             
52095             if (this.intervalID) {
52096                 // remove the timer
52097                 window.clearInterval(this.intervalID);
52098                 this.intervalID = false;
52099             }
52100             
52101             
52102             if (Roo.get('loading')) {
52103                 Roo.get('loading').remove();
52104             }
52105             if (Roo.get('loading-mask')) {
52106                 Roo.get('loading-mask').hide();
52107             }
52108             
52109             //incomming._node = tnode;
52110             this.form.reset();
52111             //this.dialog.modal = !modal;
52112             //this.dialog.show();
52113             this.el.unmask(); 
52114             
52115             
52116             this.form.setValues({
52117                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52118                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52119             });
52120             
52121             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52122             if (this.form.findField('username').getValue().length > 0 ){
52123                 this.form.findField('password').focus();
52124             } else {
52125                this.form.findField('username').focus();
52126             }
52127     
52128         }
52129     },
52130     items : [
52131          {
52132        
52133             xtype : 'ContentPanel',
52134             xns : Roo,
52135             region: 'center',
52136             fitToFrame : true,
52137             
52138             items : [
52139     
52140                 {
52141                
52142                     xtype : 'Form',
52143                     xns : Roo.form,
52144                     labelWidth: 100,
52145                     style : 'margin: 10px;',
52146                     
52147                     listeners : {
52148                         actionfailed : function(f, act) {
52149                             // form can return { errors: .... }
52150                                 
52151                             //act.result.errors // invalid form element list...
52152                             //act.result.errorMsg// invalid form element list...
52153                             
52154                             this.dialog.el.unmask();
52155                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52156                                         "Login failed - communication error - try again.");
52157                                       
52158                         },
52159                         actioncomplete: function(re, act) {
52160                              
52161                             Roo.state.Manager.set(
52162                                 this.dialog.realm + '.username',  
52163                                     this.findField('username').getValue()
52164                             );
52165                             Roo.state.Manager.set(
52166                                 this.dialog.realm + '.lang',  
52167                                 this.findField('lang').getValue() 
52168                             );
52169                             
52170                             this.dialog.fillAuth(act.result.data);
52171                               
52172                             this.dialog.hide();
52173                             
52174                             if (Roo.get('loading-mask')) {
52175                                 Roo.get('loading-mask').show();
52176                             }
52177                             Roo.XComponent.build();
52178                             
52179                              
52180                             
52181                         }
52182                     },
52183                     items : [
52184                         {
52185                             xtype : 'TextField',
52186                             xns : Roo.form,
52187                             fieldLabel: "Email Address",
52188                             name: 'username',
52189                             width:200,
52190                             autoCreate : {tag: "input", type: "text", size: "20"}
52191                         },
52192                         {
52193                             xtype : 'TextField',
52194                             xns : Roo.form,
52195                             fieldLabel: "Password",
52196                             inputType: 'password',
52197                             name: 'password',
52198                             width:200,
52199                             autoCreate : {tag: "input", type: "text", size: "20"},
52200                             listeners : {
52201                                 specialkey : function(e,ev) {
52202                                     if (ev.keyCode == 13) {
52203                                         this.form.dialog.el.mask("Logging in");
52204                                         this.form.doAction('submit', {
52205                                             url: this.form.dialog.url,
52206                                             method: this.form.dialog.method
52207                                         });
52208                                     }
52209                                 }
52210                             }  
52211                         },
52212                         {
52213                             xtype : 'ComboBox',
52214                             xns : Roo.form,
52215                             fieldLabel: "Language",
52216                             name : 'langdisp',
52217                             store: {
52218                                 xtype : 'SimpleStore',
52219                                 fields: ['lang', 'ldisp'],
52220                                 data : [
52221                                     [ 'en', 'English' ],
52222                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52223                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52224                                 ]
52225                             },
52226                             
52227                             valueField : 'lang',
52228                             hiddenName:  'lang',
52229                             width: 200,
52230                             displayField:'ldisp',
52231                             typeAhead: false,
52232                             editable: false,
52233                             mode: 'local',
52234                             triggerAction: 'all',
52235                             emptyText:'Select a Language...',
52236                             selectOnFocus:true,
52237                             listeners : {
52238                                 select :  function(cb, rec, ix) {
52239                                     this.form.switchLang(rec.data.lang);
52240                                 }
52241                             }
52242                         
52243                         }
52244                     ]
52245                 }
52246                   
52247                 
52248             ]
52249         }
52250     ],
52251     buttons : [
52252         {
52253             xtype : 'Button',
52254             xns : 'Roo',
52255             text : "Forgot Password",
52256             listeners : {
52257                 click : function() {
52258                     //console.log(this);
52259                     var n = this.form.findField('username').getValue();
52260                     if (!n.length) {
52261                         Roo.MessageBox.alert("Error", "Fill in your email address");
52262                         return;
52263                     }
52264                     Roo.Ajax.request({
52265                         url: this.dialog.url,
52266                         params: {
52267                             passwordRequest: n
52268                         },
52269                         method: this.dialog.method,
52270                         success:  function(response, opts)  {  // check successfull...
52271                         
52272                             var res = this.dialog.processResponse(response);
52273                             if (!res.success) { // error!
52274                                Roo.MessageBox.alert("Error" ,
52275                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52276                                return;
52277                             }
52278                             Roo.MessageBox.alert("Notice" ,
52279                                 "Please check you email for the Password Reset message");
52280                         },
52281                         failure : function() {
52282                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52283                         }
52284                         
52285                     });
52286                 }
52287             }
52288         },
52289         {
52290             xtype : 'Button',
52291             xns : 'Roo',
52292             text : "Login",
52293             listeners : {
52294                 
52295                 click : function () {
52296                         
52297                     this.dialog.el.mask("Logging in");
52298                     this.form.doAction('submit', {
52299                             url: this.dialog.url,
52300                             method: this.dialog.method
52301                     });
52302                 }
52303             }
52304         }
52305     ]
52306   
52307   
52308 })
52309  
52310
52311
52312