XComponent - refactoring - move rendering code into XComponent element
[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 /**
4031  * @class Roo.DomHelper
4032  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4033  * 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>.
4034  * @singleton
4035  */
4036 Roo.DomHelper = function(){
4037     var tempTableEl = null;
4038     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4039     var tableRe = /^table|tbody|tr|td$/i;
4040     var xmlns = {};
4041     // build as innerHTML where available
4042     /** @ignore */
4043     var createHtml = function(o){
4044         if(typeof o == 'string'){
4045             return o;
4046         }
4047         var b = "";
4048         if(!o.tag){
4049             o.tag = "div";
4050         }
4051         b += "<" + o.tag;
4052         for(var attr in o){
4053             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4054             if(attr == "style"){
4055                 var s = o["style"];
4056                 if(typeof s == "function"){
4057                     s = s.call();
4058                 }
4059                 if(typeof s == "string"){
4060                     b += ' style="' + s + '"';
4061                 }else if(typeof s == "object"){
4062                     b += ' style="';
4063                     for(var key in s){
4064                         if(typeof s[key] != "function"){
4065                             b += key + ":" + s[key] + ";";
4066                         }
4067                     }
4068                     b += '"';
4069                 }
4070             }else{
4071                 if(attr == "cls"){
4072                     b += ' class="' + o["cls"] + '"';
4073                 }else if(attr == "htmlFor"){
4074                     b += ' for="' + o["htmlFor"] + '"';
4075                 }else{
4076                     b += " " + attr + '="' + o[attr] + '"';
4077                 }
4078             }
4079         }
4080         if(emptyTags.test(o.tag)){
4081             b += "/>";
4082         }else{
4083             b += ">";
4084             var cn = o.children || o.cn;
4085             if(cn){
4086                 //http://bugs.kde.org/show_bug.cgi?id=71506
4087                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4088                     for(var i = 0, len = cn.length; i < len; i++) {
4089                         b += createHtml(cn[i], b);
4090                     }
4091                 }else{
4092                     b += createHtml(cn, b);
4093                 }
4094             }
4095             if(o.html){
4096                 b += o.html;
4097             }
4098             b += "</" + o.tag + ">";
4099         }
4100         return b;
4101     };
4102
4103     // build as dom
4104     /** @ignore */
4105     var createDom = function(o, parentNode){
4106          
4107         // defininition craeted..
4108         var ns = false;
4109         if (o.ns && o.ns != 'html') {
4110                
4111             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4112                 xmlns[o.ns] = o.xmlns;
4113                 ns = o.xmlns;
4114             }
4115             if (typeof(xmlns[o.ns]) == 'undefined') {
4116                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4117             }
4118             ns = xmlns[o.ns];
4119         }
4120         
4121         
4122         if (typeof(o) == 'string') {
4123             return parentNode.appendChild(document.createTextNode(o));
4124         }
4125         o.tag = o.tag || div;
4126         if (o.ns && Roo.isIE) {
4127             ns = false;
4128             o.tag = o.ns + ':' + o.tag;
4129             
4130         }
4131         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4132         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4133         for(var attr in o){
4134             
4135             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4136                     attr == "style" || typeof o[attr] == "function") continue;
4137                     
4138             if(attr=="cls" && Roo.isIE){
4139                 el.className = o["cls"];
4140             }else{
4141                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4142                 else el[attr] = o[attr];
4143             }
4144         }
4145         Roo.DomHelper.applyStyles(el, o.style);
4146         var cn = o.children || o.cn;
4147         if(cn){
4148             //http://bugs.kde.org/show_bug.cgi?id=71506
4149              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4150                 for(var i = 0, len = cn.length; i < len; i++) {
4151                     createDom(cn[i], el);
4152                 }
4153             }else{
4154                 createDom(cn, el);
4155             }
4156         }
4157         if(o.html){
4158             el.innerHTML = o.html;
4159         }
4160         if(parentNode){
4161            parentNode.appendChild(el);
4162         }
4163         return el;
4164     };
4165
4166     var ieTable = function(depth, s, h, e){
4167         tempTableEl.innerHTML = [s, h, e].join('');
4168         var i = -1, el = tempTableEl;
4169         while(++i < depth){
4170             el = el.firstChild;
4171         }
4172         return el;
4173     };
4174
4175     // kill repeat to save bytes
4176     var ts = '<table>',
4177         te = '</table>',
4178         tbs = ts+'<tbody>',
4179         tbe = '</tbody>'+te,
4180         trs = tbs + '<tr>',
4181         tre = '</tr>'+tbe;
4182
4183     /**
4184      * @ignore
4185      * Nasty code for IE's broken table implementation
4186      */
4187     var insertIntoTable = function(tag, where, el, html){
4188         if(!tempTableEl){
4189             tempTableEl = document.createElement('div');
4190         }
4191         var node;
4192         var before = null;
4193         if(tag == 'td'){
4194             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4195                 return;
4196             }
4197             if(where == 'beforebegin'){
4198                 before = el;
4199                 el = el.parentNode;
4200             } else{
4201                 before = el.nextSibling;
4202                 el = el.parentNode;
4203             }
4204             node = ieTable(4, trs, html, tre);
4205         }
4206         else if(tag == 'tr'){
4207             if(where == 'beforebegin'){
4208                 before = el;
4209                 el = el.parentNode;
4210                 node = ieTable(3, tbs, html, tbe);
4211             } else if(where == 'afterend'){
4212                 before = el.nextSibling;
4213                 el = el.parentNode;
4214                 node = ieTable(3, tbs, html, tbe);
4215             } else{ // INTO a TR
4216                 if(where == 'afterbegin'){
4217                     before = el.firstChild;
4218                 }
4219                 node = ieTable(4, trs, html, tre);
4220             }
4221         } else if(tag == 'tbody'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(2, ts, html, te);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(2, ts, html, te);
4230             } else{
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(3, tbs, html, tbe);
4235             }
4236         } else{ // TABLE
4237             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4238                 return;
4239             }
4240             if(where == 'afterbegin'){
4241                 before = el.firstChild;
4242             }
4243             node = ieTable(2, ts, html, te);
4244         }
4245         el.insertBefore(node, before);
4246         return node;
4247     };
4248
4249     return {
4250     /** True to force the use of DOM instead of html fragments @type Boolean */
4251     useDom : false,
4252
4253     /**
4254      * Returns the markup for the passed Element(s) config
4255      * @param {Object} o The Dom object spec (and children)
4256      * @return {String}
4257      */
4258     markup : function(o){
4259         return createHtml(o);
4260     },
4261
4262     /**
4263      * Applies a style specification to an element
4264      * @param {String/HTMLElement} el The element to apply styles to
4265      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4266      * a function which returns such a specification.
4267      */
4268     applyStyles : function(el, styles){
4269         if(styles){
4270            el = Roo.fly(el);
4271            if(typeof styles == "string"){
4272                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4273                var matches;
4274                while ((matches = re.exec(styles)) != null){
4275                    el.setStyle(matches[1], matches[2]);
4276                }
4277            }else if (typeof styles == "object"){
4278                for (var style in styles){
4279                   el.setStyle(style, styles[style]);
4280                }
4281            }else if (typeof styles == "function"){
4282                 Roo.DomHelper.applyStyles(el, styles.call());
4283            }
4284         }
4285     },
4286
4287     /**
4288      * Inserts an HTML fragment into the Dom
4289      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4290      * @param {HTMLElement} el The context element
4291      * @param {String} html The HTML fragmenet
4292      * @return {HTMLElement} The new node
4293      */
4294     insertHtml : function(where, el, html){
4295         where = where.toLowerCase();
4296         if(el.insertAdjacentHTML){
4297             if(tableRe.test(el.tagName)){
4298                 var rs;
4299                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4300                     return rs;
4301                 }
4302             }
4303             switch(where){
4304                 case "beforebegin":
4305                     el.insertAdjacentHTML('BeforeBegin', html);
4306                     return el.previousSibling;
4307                 case "afterbegin":
4308                     el.insertAdjacentHTML('AfterBegin', html);
4309                     return el.firstChild;
4310                 case "beforeend":
4311                     el.insertAdjacentHTML('BeforeEnd', html);
4312                     return el.lastChild;
4313                 case "afterend":
4314                     el.insertAdjacentHTML('AfterEnd', html);
4315                     return el.nextSibling;
4316             }
4317             throw 'Illegal insertion point -> "' + where + '"';
4318         }
4319         var range = el.ownerDocument.createRange();
4320         var frag;
4321         switch(where){
4322              case "beforebegin":
4323                 range.setStartBefore(el);
4324                 frag = range.createContextualFragment(html);
4325                 el.parentNode.insertBefore(frag, el);
4326                 return el.previousSibling;
4327              case "afterbegin":
4328                 if(el.firstChild){
4329                     range.setStartBefore(el.firstChild);
4330                     frag = range.createContextualFragment(html);
4331                     el.insertBefore(frag, el.firstChild);
4332                     return el.firstChild;
4333                 }else{
4334                     el.innerHTML = html;
4335                     return el.firstChild;
4336                 }
4337             case "beforeend":
4338                 if(el.lastChild){
4339                     range.setStartAfter(el.lastChild);
4340                     frag = range.createContextualFragment(html);
4341                     el.appendChild(frag);
4342                     return el.lastChild;
4343                 }else{
4344                     el.innerHTML = html;
4345                     return el.lastChild;
4346                 }
4347             case "afterend":
4348                 range.setStartAfter(el);
4349                 frag = range.createContextualFragment(html);
4350                 el.parentNode.insertBefore(frag, el.nextSibling);
4351                 return el.nextSibling;
4352             }
4353             throw 'Illegal insertion point -> "' + where + '"';
4354     },
4355
4356     /**
4357      * Creates new Dom element(s) and inserts them before el
4358      * @param {String/HTMLElement/Element} el The context element
4359      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4360      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4361      * @return {HTMLElement/Roo.Element} The new node
4362      */
4363     insertBefore : function(el, o, returnElement){
4364         return this.doInsert(el, o, returnElement, "beforeBegin");
4365     },
4366
4367     /**
4368      * Creates new Dom element(s) and inserts them after el
4369      * @param {String/HTMLElement/Element} el The context element
4370      * @param {Object} o The Dom object spec (and children)
4371      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4372      * @return {HTMLElement/Roo.Element} The new node
4373      */
4374     insertAfter : function(el, o, returnElement){
4375         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4376     },
4377
4378     /**
4379      * Creates new Dom element(s) and inserts them as the first child of el
4380      * @param {String/HTMLElement/Element} el The context element
4381      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4382      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4383      * @return {HTMLElement/Roo.Element} The new node
4384      */
4385     insertFirst : function(el, o, returnElement){
4386         return this.doInsert(el, o, returnElement, "afterBegin");
4387     },
4388
4389     // private
4390     doInsert : function(el, o, returnElement, pos, sibling){
4391         el = Roo.getDom(el);
4392         var newNode;
4393         if(this.useDom || o.ns){
4394             newNode = createDom(o, null);
4395             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4396         }else{
4397             var html = createHtml(o);
4398             newNode = this.insertHtml(pos, el, html);
4399         }
4400         return returnElement ? Roo.get(newNode, true) : newNode;
4401     },
4402
4403     /**
4404      * Creates new Dom element(s) and appends them to el
4405      * @param {String/HTMLElement/Element} el The context element
4406      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4407      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4408      * @return {HTMLElement/Roo.Element} The new node
4409      */
4410     append : function(el, o, returnElement){
4411         el = Roo.getDom(el);
4412         var newNode;
4413         if(this.useDom || o.ns){
4414             newNode = createDom(o, null);
4415             el.appendChild(newNode);
4416         }else{
4417             var html = createHtml(o);
4418             newNode = this.insertHtml("beforeEnd", el, html);
4419         }
4420         return returnElement ? Roo.get(newNode, true) : newNode;
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and overwrites the contents of el with them
4425      * @param {String/HTMLElement/Element} el The context element
4426      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4427      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4428      * @return {HTMLElement/Roo.Element} The new node
4429      */
4430     overwrite : function(el, o, returnElement){
4431         el = Roo.getDom(el);
4432         if (o.ns) {
4433           
4434             while (el.childNodes.length) {
4435                 el.removeChild(el.firstChild);
4436             }
4437             createDom(o, el);
4438         } else {
4439             el.innerHTML = createHtml(o);   
4440         }
4441         
4442         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4443     },
4444
4445     /**
4446      * Creates a new Roo.DomHelper.Template from the Dom object spec
4447      * @param {Object} o The Dom object spec (and children)
4448      * @return {Roo.DomHelper.Template} The new template
4449      */
4450     createTemplate : function(o){
4451         var html = createHtml(o);
4452         return new Roo.Template(html);
4453     }
4454     };
4455 }();
4456 /*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466  
4467 /**
4468 * @class Roo.Template
4469 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4470 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4471 * Usage:
4472 <pre><code>
4473 var t = new Roo.Template({
4474     html :  '&lt;div name="{id}"&gt;' + 
4475         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4476         '&lt;/div&gt;',
4477     myformat: function (value, allValues) {
4478         return 'XX' + value;
4479     }
4480 });
4481 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4482 </code></pre>
4483 * 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>. 
4484 * @constructor
4485 * @param {Object} cfg - Configuration object.
4486 */
4487 Roo.Template = function(cfg){
4488     // BC!
4489     if(cfg instanceof Array){
4490         cfg = cfg.join("");
4491     }else if(arguments.length > 1){
4492         cfg = Array.prototype.join.call(arguments, "");
4493     }
4494     
4495     
4496     if (typeof(cfg) == 'object') {
4497         Roo.apply(this,cfg)
4498     } else {
4499         // bc
4500         this.html = cfg;
4501     }
4502     
4503     
4504 };
4505 Roo.Template.prototype = {
4506     
4507     /**
4508      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4509      */
4510     html : '',
4511     /**
4512      * Returns an HTML fragment of this template with the specified values applied.
4513      * @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'})
4514      * @return {String} The HTML fragment
4515      */
4516     applyTemplate : function(values){
4517         try {
4518             
4519             if(this.compiled){
4520                 return this.compiled(values);
4521             }
4522             var useF = this.disableFormats !== true;
4523             var fm = Roo.util.Format, tpl = this;
4524             var fn = function(m, name, format, args){
4525                 if(format && useF){
4526                     if(format.substr(0, 5) == "this."){
4527                         return tpl.call(format.substr(5), values[name], values);
4528                     }else{
4529                         if(args){
4530                             // quoted values are required for strings in compiled templates, 
4531                             // but for non compiled we need to strip them
4532                             // quoted reversed for jsmin
4533                             var re = /^\s*['"](.*)["']\s*$/;
4534                             args = args.split(',');
4535                             for(var i = 0, len = args.length; i < len; i++){
4536                                 args[i] = args[i].replace(re, "$1");
4537                             }
4538                             args = [values[name]].concat(args);
4539                         }else{
4540                             args = [values[name]];
4541                         }
4542                         return fm[format].apply(fm, args);
4543                     }
4544                 }else{
4545                     return values[name] !== undefined ? values[name] : "";
4546                 }
4547             };
4548             return this.html.replace(this.re, fn);
4549         } catch (e) {
4550             Roo.log(e);
4551             throw e;
4552         }
4553          
4554     },
4555     
4556     /**
4557      * Sets the HTML used as the template and optionally compiles it.
4558      * @param {String} html
4559      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4560      * @return {Roo.Template} this
4561      */
4562     set : function(html, compile){
4563         this.html = html;
4564         this.compiled = null;
4565         if(compile){
4566             this.compile();
4567         }
4568         return this;
4569     },
4570     
4571     /**
4572      * True to disable format functions (defaults to false)
4573      * @type Boolean
4574      */
4575     disableFormats : false,
4576     
4577     /**
4578     * The regular expression used to match template variables 
4579     * @type RegExp
4580     * @property 
4581     */
4582     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4583     
4584     /**
4585      * Compiles the template into an internal function, eliminating the RegEx overhead.
4586      * @return {Roo.Template} this
4587      */
4588     compile : function(){
4589         var fm = Roo.util.Format;
4590         var useF = this.disableFormats !== true;
4591         var sep = Roo.isGecko ? "+" : ",";
4592         var fn = function(m, name, format, args){
4593             if(format && useF){
4594                 args = args ? ',' + args : "";
4595                 if(format.substr(0, 5) != "this."){
4596                     format = "fm." + format + '(';
4597                 }else{
4598                     format = 'this.call("'+ format.substr(5) + '", ';
4599                     args = ", values";
4600                 }
4601             }else{
4602                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4603             }
4604             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4605         };
4606         var body;
4607         // branched to use + in gecko and [].join() in others
4608         if(Roo.isGecko){
4609             body = "this.compiled = function(values){ return '" +
4610                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4611                     "';};";
4612         }else{
4613             body = ["this.compiled = function(values){ return ['"];
4614             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4615             body.push("'].join('');};");
4616             body = body.join('');
4617         }
4618         /**
4619          * eval:var:values
4620          * eval:var:fm
4621          */
4622         eval(body);
4623         return this;
4624     },
4625     
4626     // private function used to call members
4627     call : function(fnName, value, allValues){
4628         return this[fnName](value, allValues);
4629     },
4630     
4631     /**
4632      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4633      * @param {String/HTMLElement/Roo.Element} el The context element
4634      * @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'})
4635      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4636      * @return {HTMLElement/Roo.Element} The new node or Element
4637      */
4638     insertFirst: function(el, values, returnElement){
4639         return this.doInsert('afterBegin', el, values, returnElement);
4640     },
4641
4642     /**
4643      * Applies the supplied values to the template and inserts the new node(s) before el.
4644      * @param {String/HTMLElement/Roo.Element} el The context element
4645      * @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'})
4646      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4647      * @return {HTMLElement/Roo.Element} The new node or Element
4648      */
4649     insertBefore: function(el, values, returnElement){
4650         return this.doInsert('beforeBegin', el, values, returnElement);
4651     },
4652
4653     /**
4654      * Applies the supplied values to the template and inserts the new node(s) after el.
4655      * @param {String/HTMLElement/Roo.Element} el The context element
4656      * @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'})
4657      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4658      * @return {HTMLElement/Roo.Element} The new node or Element
4659      */
4660     insertAfter : function(el, values, returnElement){
4661         return this.doInsert('afterEnd', el, values, returnElement);
4662     },
4663     
4664     /**
4665      * Applies the supplied values to the template and appends the new node(s) to el.
4666      * @param {String/HTMLElement/Roo.Element} el The context element
4667      * @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'})
4668      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4669      * @return {HTMLElement/Roo.Element} The new node or Element
4670      */
4671     append : function(el, values, returnElement){
4672         return this.doInsert('beforeEnd', el, values, returnElement);
4673     },
4674
4675     doInsert : function(where, el, values, returnEl){
4676         el = Roo.getDom(el);
4677         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4678         return returnEl ? Roo.get(newNode, true) : newNode;
4679     },
4680
4681     /**
4682      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4683      * @param {String/HTMLElement/Roo.Element} el The context element
4684      * @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'})
4685      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4686      * @return {HTMLElement/Roo.Element} The new node or Element
4687      */
4688     overwrite : function(el, values, returnElement){
4689         el = Roo.getDom(el);
4690         el.innerHTML = this.applyTemplate(values);
4691         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4692     }
4693 };
4694 /**
4695  * Alias for {@link #applyTemplate}
4696  * @method
4697  */
4698 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4699
4700 // backwards compat
4701 Roo.DomHelper.Template = Roo.Template;
4702
4703 /**
4704  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4705  * @param {String/HTMLElement} el A DOM element or its id
4706  * @returns {Roo.Template} The created template
4707  * @static
4708  */
4709 Roo.Template.from = function(el){
4710     el = Roo.getDom(el);
4711     return new Roo.Template(el.value || el.innerHTML);
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722  
4723
4724 /*
4725  * This is code is also distributed under MIT license for use
4726  * with jQuery and prototype JavaScript libraries.
4727  */
4728 /**
4729  * @class Roo.DomQuery
4730 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).
4731 <p>
4732 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>
4733
4734 <p>
4735 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.
4736 </p>
4737 <h4>Element Selectors:</h4>
4738 <ul class="list">
4739     <li> <b>*</b> any element</li>
4740     <li> <b>E</b> an element with the tag E</li>
4741     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4742     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4743     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4744     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4745 </ul>
4746 <h4>Attribute Selectors:</h4>
4747 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4748 <ul class="list">
4749     <li> <b>E[foo]</b> has an attribute "foo"</li>
4750     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4751     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4752     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4753     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4754     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4755     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4756 </ul>
4757 <h4>Pseudo Classes:</h4>
4758 <ul class="list">
4759     <li> <b>E:first-child</b> E is the first child of its parent</li>
4760     <li> <b>E:last-child</b> E is the last child of its parent</li>
4761     <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>
4762     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4763     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4764     <li> <b>E:only-child</b> E is the only child of its parent</li>
4765     <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>
4766     <li> <b>E:first</b> the first E in the resultset</li>
4767     <li> <b>E:last</b> the last E in the resultset</li>
4768     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4769     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4770     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4771     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4772     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4773     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4774     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4775     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4776     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4777 </ul>
4778 <h4>CSS Value Selectors:</h4>
4779 <ul class="list">
4780     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4781     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4782     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4783     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4784     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4785     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4786 </ul>
4787  * @singleton
4788  */
4789 Roo.DomQuery = function(){
4790     var cache = {}, simpleCache = {}, valueCache = {};
4791     var nonSpace = /\S/;
4792     var trimRe = /^\s+|\s+$/g;
4793     var tplRe = /\{(\d+)\}/g;
4794     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4795     var tagTokenRe = /^(#)?([\w-\*]+)/;
4796     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4797
4798     function child(p, index){
4799         var i = 0;
4800         var n = p.firstChild;
4801         while(n){
4802             if(n.nodeType == 1){
4803                if(++i == index){
4804                    return n;
4805                }
4806             }
4807             n = n.nextSibling;
4808         }
4809         return null;
4810     };
4811
4812     function next(n){
4813         while((n = n.nextSibling) && n.nodeType != 1);
4814         return n;
4815     };
4816
4817     function prev(n){
4818         while((n = n.previousSibling) && n.nodeType != 1);
4819         return n;
4820     };
4821
4822     function children(d){
4823         var n = d.firstChild, ni = -1;
4824             while(n){
4825                 var nx = n.nextSibling;
4826                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4827                     d.removeChild(n);
4828                 }else{
4829                     n.nodeIndex = ++ni;
4830                 }
4831                 n = nx;
4832             }
4833             return this;
4834         };
4835
4836     function byClassName(c, a, v){
4837         if(!v){
4838             return c;
4839         }
4840         var r = [], ri = -1, cn;
4841         for(var i = 0, ci; ci = c[i]; i++){
4842             if((' '+ci.className+' ').indexOf(v) != -1){
4843                 r[++ri] = ci;
4844             }
4845         }
4846         return r;
4847     };
4848
4849     function attrValue(n, attr){
4850         if(!n.tagName && typeof n.length != "undefined"){
4851             n = n[0];
4852         }
4853         if(!n){
4854             return null;
4855         }
4856         if(attr == "for"){
4857             return n.htmlFor;
4858         }
4859         if(attr == "class" || attr == "className"){
4860             return n.className;
4861         }
4862         return n.getAttribute(attr) || n[attr];
4863
4864     };
4865
4866     function getNodes(ns, mode, tagName){
4867         var result = [], ri = -1, cs;
4868         if(!ns){
4869             return result;
4870         }
4871         tagName = tagName || "*";
4872         if(typeof ns.getElementsByTagName != "undefined"){
4873             ns = [ns];
4874         }
4875         if(!mode){
4876             for(var i = 0, ni; ni = ns[i]; i++){
4877                 cs = ni.getElementsByTagName(tagName);
4878                 for(var j = 0, ci; ci = cs[j]; j++){
4879                     result[++ri] = ci;
4880                 }
4881             }
4882         }else if(mode == "/" || mode == ">"){
4883             var utag = tagName.toUpperCase();
4884             for(var i = 0, ni, cn; ni = ns[i]; i++){
4885                 cn = ni.children || ni.childNodes;
4886                 for(var j = 0, cj; cj = cn[j]; j++){
4887                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4888                         result[++ri] = cj;
4889                     }
4890                 }
4891             }
4892         }else if(mode == "+"){
4893             var utag = tagName.toUpperCase();
4894             for(var i = 0, n; n = ns[i]; i++){
4895                 while((n = n.nextSibling) && n.nodeType != 1);
4896                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4897                     result[++ri] = n;
4898                 }
4899             }
4900         }else if(mode == "~"){
4901             for(var i = 0, n; n = ns[i]; i++){
4902                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4903                 if(n){
4904                     result[++ri] = n;
4905                 }
4906             }
4907         }
4908         return result;
4909     };
4910
4911     function concat(a, b){
4912         if(b.slice){
4913             return a.concat(b);
4914         }
4915         for(var i = 0, l = b.length; i < l; i++){
4916             a[a.length] = b[i];
4917         }
4918         return a;
4919     }
4920
4921     function byTag(cs, tagName){
4922         if(cs.tagName || cs == document){
4923             cs = [cs];
4924         }
4925         if(!tagName){
4926             return cs;
4927         }
4928         var r = [], ri = -1;
4929         tagName = tagName.toLowerCase();
4930         for(var i = 0, ci; ci = cs[i]; i++){
4931             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4932                 r[++ri] = ci;
4933             }
4934         }
4935         return r;
4936     };
4937
4938     function byId(cs, attr, id){
4939         if(cs.tagName || cs == document){
4940             cs = [cs];
4941         }
4942         if(!id){
4943             return cs;
4944         }
4945         var r = [], ri = -1;
4946         for(var i = 0,ci; ci = cs[i]; i++){
4947             if(ci && ci.id == id){
4948                 r[++ri] = ci;
4949                 return r;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function byAttribute(cs, attr, value, op, custom){
4956         var r = [], ri = -1, st = custom=="{";
4957         var f = Roo.DomQuery.operators[op];
4958         for(var i = 0, ci; ci = cs[i]; i++){
4959             var a;
4960             if(st){
4961                 a = Roo.DomQuery.getStyle(ci, attr);
4962             }
4963             else if(attr == "class" || attr == "className"){
4964                 a = ci.className;
4965             }else if(attr == "for"){
4966                 a = ci.htmlFor;
4967             }else if(attr == "href"){
4968                 a = ci.getAttribute("href", 2);
4969             }else{
4970                 a = ci.getAttribute(attr);
4971             }
4972             if((f && f(a, value)) || (!f && a)){
4973                 r[++ri] = ci;
4974             }
4975         }
4976         return r;
4977     };
4978
4979     function byPseudo(cs, name, value){
4980         return Roo.DomQuery.pseudos[name](cs, value);
4981     };
4982
4983     // This is for IE MSXML which does not support expandos.
4984     // IE runs the same speed using setAttribute, however FF slows way down
4985     // and Safari completely fails so they need to continue to use expandos.
4986     var isIE = window.ActiveXObject ? true : false;
4987
4988     // this eval is stop the compressor from
4989     // renaming the variable to something shorter
4990     
4991     /** eval:var:batch */
4992     var batch = 30803; 
4993
4994     var key = 30803;
4995
4996     function nodupIEXml(cs){
4997         var d = ++key;
4998         cs[0].setAttribute("_nodup", d);
4999         var r = [cs[0]];
5000         for(var i = 1, len = cs.length; i < len; i++){
5001             var c = cs[i];
5002             if(!c.getAttribute("_nodup") != d){
5003                 c.setAttribute("_nodup", d);
5004                 r[r.length] = c;
5005             }
5006         }
5007         for(var i = 0, len = cs.length; i < len; i++){
5008             cs[i].removeAttribute("_nodup");
5009         }
5010         return r;
5011     }
5012
5013     function nodup(cs){
5014         if(!cs){
5015             return [];
5016         }
5017         var len = cs.length, c, i, r = cs, cj, ri = -1;
5018         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5019             return cs;
5020         }
5021         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5022             return nodupIEXml(cs);
5023         }
5024         var d = ++key;
5025         cs[0]._nodup = d;
5026         for(i = 1; c = cs[i]; i++){
5027             if(c._nodup != d){
5028                 c._nodup = d;
5029             }else{
5030                 r = [];
5031                 for(var j = 0; j < i; j++){
5032                     r[++ri] = cs[j];
5033                 }
5034                 for(j = i+1; cj = cs[j]; j++){
5035                     if(cj._nodup != d){
5036                         cj._nodup = d;
5037                         r[++ri] = cj;
5038                     }
5039                 }
5040                 return r;
5041             }
5042         }
5043         return r;
5044     }
5045
5046     function quickDiffIEXml(c1, c2){
5047         var d = ++key;
5048         for(var i = 0, len = c1.length; i < len; i++){
5049             c1[i].setAttribute("_qdiff", d);
5050         }
5051         var r = [];
5052         for(var i = 0, len = c2.length; i < len; i++){
5053             if(c2[i].getAttribute("_qdiff") != d){
5054                 r[r.length] = c2[i];
5055             }
5056         }
5057         for(var i = 0, len = c1.length; i < len; i++){
5058            c1[i].removeAttribute("_qdiff");
5059         }
5060         return r;
5061     }
5062
5063     function quickDiff(c1, c2){
5064         var len1 = c1.length;
5065         if(!len1){
5066             return c2;
5067         }
5068         if(isIE && c1[0].selectSingleNode){
5069             return quickDiffIEXml(c1, c2);
5070         }
5071         var d = ++key;
5072         for(var i = 0; i < len1; i++){
5073             c1[i]._qdiff = d;
5074         }
5075         var r = [];
5076         for(var i = 0, len = c2.length; i < len; i++){
5077             if(c2[i]._qdiff != d){
5078                 r[r.length] = c2[i];
5079             }
5080         }
5081         return r;
5082     }
5083
5084     function quickId(ns, mode, root, id){
5085         if(ns == root){
5086            var d = root.ownerDocument || root;
5087            return d.getElementById(id);
5088         }
5089         ns = getNodes(ns, mode, "*");
5090         return byId(ns, null, id);
5091     }
5092
5093     return {
5094         getStyle : function(el, name){
5095             return Roo.fly(el).getStyle(name);
5096         },
5097         /**
5098          * Compiles a selector/xpath query into a reusable function. The returned function
5099          * takes one parameter "root" (optional), which is the context node from where the query should start.
5100          * @param {String} selector The selector/xpath query
5101          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5102          * @return {Function}
5103          */
5104         compile : function(path, type){
5105             type = type || "select";
5106             
5107             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5108             var q = path, mode, lq;
5109             var tk = Roo.DomQuery.matchers;
5110             var tklen = tk.length;
5111             var mm;
5112
5113             // accept leading mode switch
5114             var lmode = q.match(modeRe);
5115             if(lmode && lmode[1]){
5116                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5117                 q = q.replace(lmode[1], "");
5118             }
5119             // strip leading slashes
5120             while(path.substr(0, 1)=="/"){
5121                 path = path.substr(1);
5122             }
5123
5124             while(q && lq != q){
5125                 lq = q;
5126                 var tm = q.match(tagTokenRe);
5127                 if(type == "select"){
5128                     if(tm){
5129                         if(tm[1] == "#"){
5130                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5131                         }else{
5132                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5133                         }
5134                         q = q.replace(tm[0], "");
5135                     }else if(q.substr(0, 1) != '@'){
5136                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5137                     }
5138                 }else{
5139                     if(tm){
5140                         if(tm[1] == "#"){
5141                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5142                         }else{
5143                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5144                         }
5145                         q = q.replace(tm[0], "");
5146                     }
5147                 }
5148                 while(!(mm = q.match(modeRe))){
5149                     var matched = false;
5150                     for(var j = 0; j < tklen; j++){
5151                         var t = tk[j];
5152                         var m = q.match(t.re);
5153                         if(m){
5154                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5155                                                     return m[i];
5156                                                 });
5157                             q = q.replace(m[0], "");
5158                             matched = true;
5159                             break;
5160                         }
5161                     }
5162                     // prevent infinite loop on bad selector
5163                     if(!matched){
5164                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5165                     }
5166                 }
5167                 if(mm[1]){
5168                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5169                     q = q.replace(mm[1], "");
5170                 }
5171             }
5172             fn[fn.length] = "return nodup(n);\n}";
5173             
5174              /** 
5175               * list of variables that need from compression as they are used by eval.
5176              *  eval:var:batch 
5177              *  eval:var:nodup
5178              *  eval:var:byTag
5179              *  eval:var:ById
5180              *  eval:var:getNodes
5181              *  eval:var:quickId
5182              *  eval:var:mode
5183              *  eval:var:root
5184              *  eval:var:n
5185              *  eval:var:byClassName
5186              *  eval:var:byPseudo
5187              *  eval:var:byAttribute
5188              *  eval:var:attrValue
5189              * 
5190              **/ 
5191             eval(fn.join(""));
5192             return f;
5193         },
5194
5195         /**
5196          * Selects a group of elements.
5197          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5198          * @param {Node} root (optional) The start of the query (defaults to document).
5199          * @return {Array}
5200          */
5201         select : function(path, root, type){
5202             if(!root || root == document){
5203                 root = document;
5204             }
5205             if(typeof root == "string"){
5206                 root = document.getElementById(root);
5207             }
5208             var paths = path.split(",");
5209             var results = [];
5210             for(var i = 0, len = paths.length; i < len; i++){
5211                 var p = paths[i].replace(trimRe, "");
5212                 if(!cache[p]){
5213                     cache[p] = Roo.DomQuery.compile(p);
5214                     if(!cache[p]){
5215                         throw p + " is not a valid selector";
5216                     }
5217                 }
5218                 var result = cache[p](root);
5219                 if(result && result != document){
5220                     results = results.concat(result);
5221                 }
5222             }
5223             if(paths.length > 1){
5224                 return nodup(results);
5225             }
5226             return results;
5227         },
5228
5229         /**
5230          * Selects a single element.
5231          * @param {String} selector The selector/xpath query
5232          * @param {Node} root (optional) The start of the query (defaults to document).
5233          * @return {Element}
5234          */
5235         selectNode : function(path, root){
5236             return Roo.DomQuery.select(path, root)[0];
5237         },
5238
5239         /**
5240          * Selects the value of a node, optionally replacing null with the defaultValue.
5241          * @param {String} selector The selector/xpath query
5242          * @param {Node} root (optional) The start of the query (defaults to document).
5243          * @param {String} defaultValue
5244          */
5245         selectValue : function(path, root, defaultValue){
5246             path = path.replace(trimRe, "");
5247             if(!valueCache[path]){
5248                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5249             }
5250             var n = valueCache[path](root);
5251             n = n[0] ? n[0] : n;
5252             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5253             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5254         },
5255
5256         /**
5257          * Selects the value of a node, parsing integers and floats.
5258          * @param {String} selector The selector/xpath query
5259          * @param {Node} root (optional) The start of the query (defaults to document).
5260          * @param {Number} defaultValue
5261          * @return {Number}
5262          */
5263         selectNumber : function(path, root, defaultValue){
5264             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5265             return parseFloat(v);
5266         },
5267
5268         /**
5269          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5270          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5271          * @param {String} selector The simple selector to test
5272          * @return {Boolean}
5273          */
5274         is : function(el, ss){
5275             if(typeof el == "string"){
5276                 el = document.getElementById(el);
5277             }
5278             var isArray = (el instanceof Array);
5279             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5280             return isArray ? (result.length == el.length) : (result.length > 0);
5281         },
5282
5283         /**
5284          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5285          * @param {Array} el An array of elements to filter
5286          * @param {String} selector The simple selector to test
5287          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5288          * the selector instead of the ones that match
5289          * @return {Array}
5290          */
5291         filter : function(els, ss, nonMatches){
5292             ss = ss.replace(trimRe, "");
5293             if(!simpleCache[ss]){
5294                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5295             }
5296             var result = simpleCache[ss](els);
5297             return nonMatches ? quickDiff(result, els) : result;
5298         },
5299
5300         /**
5301          * Collection of matching regular expressions and code snippets.
5302          */
5303         matchers : [{
5304                 re: /^\.([\w-]+)/,
5305                 select: 'n = byClassName(n, null, " {1} ");'
5306             }, {
5307                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5308                 select: 'n = byPseudo(n, "{1}", "{2}");'
5309             },{
5310                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5311                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5312             }, {
5313                 re: /^#([\w-]+)/,
5314                 select: 'n = byId(n, null, "{1}");'
5315             },{
5316                 re: /^@([\w-]+)/,
5317                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5318             }
5319         ],
5320
5321         /**
5322          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5323          * 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;.
5324          */
5325         operators : {
5326             "=" : function(a, v){
5327                 return a == v;
5328             },
5329             "!=" : function(a, v){
5330                 return a != v;
5331             },
5332             "^=" : function(a, v){
5333                 return a && a.substr(0, v.length) == v;
5334             },
5335             "$=" : function(a, v){
5336                 return a && a.substr(a.length-v.length) == v;
5337             },
5338             "*=" : function(a, v){
5339                 return a && a.indexOf(v) !== -1;
5340             },
5341             "%=" : function(a, v){
5342                 return (a % v) == 0;
5343             },
5344             "|=" : function(a, v){
5345                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5346             },
5347             "~=" : function(a, v){
5348                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5349             }
5350         },
5351
5352         /**
5353          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5354          * and the argument (if any) supplied in the selector.
5355          */
5356         pseudos : {
5357             "first-child" : function(c){
5358                 var r = [], ri = -1, n;
5359                 for(var i = 0, ci; ci = n = c[i]; i++){
5360                     while((n = n.previousSibling) && n.nodeType != 1);
5361                     if(!n){
5362                         r[++ri] = ci;
5363                     }
5364                 }
5365                 return r;
5366             },
5367
5368             "last-child" : function(c){
5369                 var r = [], ri = -1, n;
5370                 for(var i = 0, ci; ci = n = c[i]; i++){
5371                     while((n = n.nextSibling) && n.nodeType != 1);
5372                     if(!n){
5373                         r[++ri] = ci;
5374                     }
5375                 }
5376                 return r;
5377             },
5378
5379             "nth-child" : function(c, a) {
5380                 var r = [], ri = -1;
5381                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5382                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5383                 for(var i = 0, n; n = c[i]; i++){
5384                     var pn = n.parentNode;
5385                     if (batch != pn._batch) {
5386                         var j = 0;
5387                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5388                             if(cn.nodeType == 1){
5389                                cn.nodeIndex = ++j;
5390                             }
5391                         }
5392                         pn._batch = batch;
5393                     }
5394                     if (f == 1) {
5395                         if (l == 0 || n.nodeIndex == l){
5396                             r[++ri] = n;
5397                         }
5398                     } else if ((n.nodeIndex + l) % f == 0){
5399                         r[++ri] = n;
5400                     }
5401                 }
5402
5403                 return r;
5404             },
5405
5406             "only-child" : function(c){
5407                 var r = [], ri = -1;;
5408                 for(var i = 0, ci; ci = c[i]; i++){
5409                     if(!prev(ci) && !next(ci)){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "empty" : function(c){
5417                 var r = [], ri = -1;
5418                 for(var i = 0, ci; ci = c[i]; i++){
5419                     var cns = ci.childNodes, j = 0, cn, empty = true;
5420                     while(cn = cns[j]){
5421                         ++j;
5422                         if(cn.nodeType == 1 || cn.nodeType == 3){
5423                             empty = false;
5424                             break;
5425                         }
5426                     }
5427                     if(empty){
5428                         r[++ri] = ci;
5429                     }
5430                 }
5431                 return r;
5432             },
5433
5434             "contains" : function(c, v){
5435                 var r = [], ri = -1;
5436                 for(var i = 0, ci; ci = c[i]; i++){
5437                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5438                         r[++ri] = ci;
5439                     }
5440                 }
5441                 return r;
5442             },
5443
5444             "nodeValue" : function(c, v){
5445                 var r = [], ri = -1;
5446                 for(var i = 0, ci; ci = c[i]; i++){
5447                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5448                         r[++ri] = ci;
5449                     }
5450                 }
5451                 return r;
5452             },
5453
5454             "checked" : function(c){
5455                 var r = [], ri = -1;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(ci.checked == true){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "not" : function(c, ss){
5465                 return Roo.DomQuery.filter(c, ss, true);
5466             },
5467
5468             "odd" : function(c){
5469                 return this["nth-child"](c, "odd");
5470             },
5471
5472             "even" : function(c){
5473                 return this["nth-child"](c, "even");
5474             },
5475
5476             "nth" : function(c, a){
5477                 return c[a-1] || [];
5478             },
5479
5480             "first" : function(c){
5481                 return c[0] || [];
5482             },
5483
5484             "last" : function(c){
5485                 return c[c.length-1] || [];
5486             },
5487
5488             "has" : function(c, ss){
5489                 var s = Roo.DomQuery.select;
5490                 var r = [], ri = -1;
5491                 for(var i = 0, ci; ci = c[i]; i++){
5492                     if(s(ss, ci).length > 0){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "next" : function(c, ss){
5500                 var is = Roo.DomQuery.is;
5501                 var r = [], ri = -1;
5502                 for(var i = 0, ci; ci = c[i]; i++){
5503                     var n = next(ci);
5504                     if(n && is(n, ss)){
5505                         r[++ri] = ci;
5506                     }
5507                 }
5508                 return r;
5509             },
5510
5511             "prev" : function(c, ss){
5512                 var is = Roo.DomQuery.is;
5513                 var r = [], ri = -1;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     var n = prev(ci);
5516                     if(n && is(n, ss)){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             }
5522         }
5523     };
5524 }();
5525
5526 /**
5527  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5528  * @param {String} path The selector/xpath query
5529  * @param {Node} root (optional) The start of the query (defaults to document).
5530  * @return {Array}
5531  * @member Roo
5532  * @method query
5533  */
5534 Roo.query = Roo.DomQuery.select;
5535 /*
5536  * Based on:
5537  * Ext JS Library 1.1.1
5538  * Copyright(c) 2006-2007, Ext JS, LLC.
5539  *
5540  * Originally Released Under LGPL - original licence link has changed is not relivant.
5541  *
5542  * Fork - LGPL
5543  * <script type="text/javascript">
5544  */
5545
5546 /**
5547  * @class Roo.util.Observable
5548  * Base class that provides a common interface for publishing events. Subclasses are expected to
5549  * to have a property "events" with all the events defined.<br>
5550  * For example:
5551  * <pre><code>
5552  Employee = function(name){
5553     this.name = name;
5554     this.addEvents({
5555         "fired" : true,
5556         "quit" : true
5557     });
5558  }
5559  Roo.extend(Employee, Roo.util.Observable);
5560 </code></pre>
5561  * @param {Object} config properties to use (incuding events / listeners)
5562  */
5563
5564 Roo.util.Observable = function(cfg){
5565     
5566     cfg = cfg|| {};
5567     this.addEvents(cfg.events || {});
5568     if (cfg.events) {
5569         delete cfg.events; // make sure
5570     }
5571      
5572     Roo.apply(this, cfg);
5573     
5574     if(this.listeners){
5575         this.on(this.listeners);
5576         delete this.listeners;
5577     }
5578 };
5579 Roo.util.Observable.prototype = {
5580     /** 
5581  * @cfg {Object} listeners  list of events and functions to call for this object, 
5582  * For example :
5583  * <pre><code>
5584     listeners :  { 
5585        'click' : function(e) {
5586            ..... 
5587         } ,
5588         .... 
5589     } 
5590   </code></pre>
5591  */
5592     
5593     
5594     /**
5595      * Fires the specified event with the passed parameters (minus the event name).
5596      * @param {String} eventName
5597      * @param {Object...} args Variable number of parameters are passed to handlers
5598      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5599      */
5600     fireEvent : function(){
5601         var ce = this.events[arguments[0].toLowerCase()];
5602         if(typeof ce == "object"){
5603             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5604         }else{
5605             return true;
5606         }
5607     },
5608
5609     // private
5610     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5611
5612     /**
5613      * Appends an event handler to this component
5614      * @param {String}   eventName The type of event to listen for
5615      * @param {Function} handler The method the event invokes
5616      * @param {Object}   scope (optional) The scope in which to execute the handler
5617      * function. The handler function's "this" context.
5618      * @param {Object}   options (optional) An object containing handler configuration
5619      * properties. This may contain any of the following properties:<ul>
5620      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5621      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5622      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5623      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5624      * by the specified number of milliseconds. If the event fires again within that time, the original
5625      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5626      * </ul><br>
5627      * <p>
5628      * <b>Combining Options</b><br>
5629      * Using the options argument, it is possible to combine different types of listeners:<br>
5630      * <br>
5631      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5632                 <pre><code>
5633                 el.on('click', this.onClick, this, {
5634                         single: true,
5635                 delay: 100,
5636                 forumId: 4
5637                 });
5638                 </code></pre>
5639      * <p>
5640      * <b>Attaching multiple handlers in 1 call</b><br>
5641      * The method also allows for a single argument to be passed which is a config object containing properties
5642      * which specify multiple handlers.
5643      * <pre><code>
5644                 el.on({
5645                         'click': {
5646                         fn: this.onClick,
5647                         scope: this,
5648                         delay: 100
5649                 }, 
5650                 'mouseover': {
5651                         fn: this.onMouseOver,
5652                         scope: this
5653                 },
5654                 'mouseout': {
5655                         fn: this.onMouseOut,
5656                         scope: this
5657                 }
5658                 });
5659                 </code></pre>
5660      * <p>
5661      * Or a shorthand syntax which passes the same scope object to all handlers:
5662         <pre><code>
5663                 el.on({
5664                         'click': this.onClick,
5665                 'mouseover': this.onMouseOver,
5666                 'mouseout': this.onMouseOut,
5667                 scope: this
5668                 });
5669                 </code></pre>
5670      */
5671     addListener : function(eventName, fn, scope, o){
5672         if(typeof eventName == "object"){
5673             o = eventName;
5674             for(var e in o){
5675                 if(this.filterOptRe.test(e)){
5676                     continue;
5677                 }
5678                 if(typeof o[e] == "function"){
5679                     // shared options
5680                     this.addListener(e, o[e], o.scope,  o);
5681                 }else{
5682                     // individual options
5683                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5684                 }
5685             }
5686             return;
5687         }
5688         o = (!o || typeof o == "boolean") ? {} : o;
5689         eventName = eventName.toLowerCase();
5690         var ce = this.events[eventName] || true;
5691         if(typeof ce == "boolean"){
5692             ce = new Roo.util.Event(this, eventName);
5693             this.events[eventName] = ce;
5694         }
5695         ce.addListener(fn, scope, o);
5696     },
5697
5698     /**
5699      * Removes a listener
5700      * @param {String}   eventName     The type of event to listen for
5701      * @param {Function} handler        The handler to remove
5702      * @param {Object}   scope  (optional) The scope (this object) for the handler
5703      */
5704     removeListener : function(eventName, fn, scope){
5705         var ce = this.events[eventName.toLowerCase()];
5706         if(typeof ce == "object"){
5707             ce.removeListener(fn, scope);
5708         }
5709     },
5710
5711     /**
5712      * Removes all listeners for this object
5713      */
5714     purgeListeners : function(){
5715         for(var evt in this.events){
5716             if(typeof this.events[evt] == "object"){
5717                  this.events[evt].clearListeners();
5718             }
5719         }
5720     },
5721
5722     relayEvents : function(o, events){
5723         var createHandler = function(ename){
5724             return function(){
5725                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5726             };
5727         };
5728         for(var i = 0, len = events.length; i < len; i++){
5729             var ename = events[i];
5730             if(!this.events[ename]){ this.events[ename] = true; };
5731             o.on(ename, createHandler(ename), this);
5732         }
5733     },
5734
5735     /**
5736      * Used to define events on this Observable
5737      * @param {Object} object The object with the events defined
5738      */
5739     addEvents : function(o){
5740         if(!this.events){
5741             this.events = {};
5742         }
5743         Roo.applyIf(this.events, o);
5744     },
5745
5746     /**
5747      * Checks to see if this object has any listeners for a specified event
5748      * @param {String} eventName The name of the event to check for
5749      * @return {Boolean} True if the event is being listened for, else false
5750      */
5751     hasListener : function(eventName){
5752         var e = this.events[eventName];
5753         return typeof e == "object" && e.listeners.length > 0;
5754     }
5755 };
5756 /**
5757  * Appends an event handler to this element (shorthand for addListener)
5758  * @param {String}   eventName     The type of event to listen for
5759  * @param {Function} handler        The method the event invokes
5760  * @param {Object}   scope (optional) The scope in which to execute the handler
5761  * function. The handler function's "this" context.
5762  * @param {Object}   options  (optional)
5763  * @method
5764  */
5765 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5766 /**
5767  * Removes a listener (shorthand for removeListener)
5768  * @param {String}   eventName     The type of event to listen for
5769  * @param {Function} handler        The handler to remove
5770  * @param {Object}   scope  (optional) The scope (this object) for the handler
5771  * @method
5772  */
5773 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5774
5775 /**
5776  * Starts capture on the specified Observable. All events will be passed
5777  * to the supplied function with the event name + standard signature of the event
5778  * <b>before</b> the event is fired. If the supplied function returns false,
5779  * the event will not fire.
5780  * @param {Observable} o The Observable to capture
5781  * @param {Function} fn The function to call
5782  * @param {Object} scope (optional) The scope (this object) for the fn
5783  * @static
5784  */
5785 Roo.util.Observable.capture = function(o, fn, scope){
5786     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5787 };
5788
5789 /**
5790  * Removes <b>all</b> added captures from the Observable.
5791  * @param {Observable} o The Observable to release
5792  * @static
5793  */
5794 Roo.util.Observable.releaseCapture = function(o){
5795     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5796 };
5797
5798 (function(){
5799
5800     var createBuffered = function(h, o, scope){
5801         var task = new Roo.util.DelayedTask();
5802         return function(){
5803             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5804         };
5805     };
5806
5807     var createSingle = function(h, e, fn, scope){
5808         return function(){
5809             e.removeListener(fn, scope);
5810             return h.apply(scope, arguments);
5811         };
5812     };
5813
5814     var createDelayed = function(h, o, scope){
5815         return function(){
5816             var args = Array.prototype.slice.call(arguments, 0);
5817             setTimeout(function(){
5818                 h.apply(scope, args);
5819             }, o.delay || 10);
5820         };
5821     };
5822
5823     Roo.util.Event = function(obj, name){
5824         this.name = name;
5825         this.obj = obj;
5826         this.listeners = [];
5827     };
5828
5829     Roo.util.Event.prototype = {
5830         addListener : function(fn, scope, options){
5831             var o = options || {};
5832             scope = scope || this.obj;
5833             if(!this.isListening(fn, scope)){
5834                 var l = {fn: fn, scope: scope, options: o};
5835                 var h = fn;
5836                 if(o.delay){
5837                     h = createDelayed(h, o, scope);
5838                 }
5839                 if(o.single){
5840                     h = createSingle(h, this, fn, scope);
5841                 }
5842                 if(o.buffer){
5843                     h = createBuffered(h, o, scope);
5844                 }
5845                 l.fireFn = h;
5846                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5847                     this.listeners.push(l);
5848                 }else{
5849                     this.listeners = this.listeners.slice(0);
5850                     this.listeners.push(l);
5851                 }
5852             }
5853         },
5854
5855         findListener : function(fn, scope){
5856             scope = scope || this.obj;
5857             var ls = this.listeners;
5858             for(var i = 0, len = ls.length; i < len; i++){
5859                 var l = ls[i];
5860                 if(l.fn == fn && l.scope == scope){
5861                     return i;
5862                 }
5863             }
5864             return -1;
5865         },
5866
5867         isListening : function(fn, scope){
5868             return this.findListener(fn, scope) != -1;
5869         },
5870
5871         removeListener : function(fn, scope){
5872             var index;
5873             if((index = this.findListener(fn, scope)) != -1){
5874                 if(!this.firing){
5875                     this.listeners.splice(index, 1);
5876                 }else{
5877                     this.listeners = this.listeners.slice(0);
5878                     this.listeners.splice(index, 1);
5879                 }
5880                 return true;
5881             }
5882             return false;
5883         },
5884
5885         clearListeners : function(){
5886             this.listeners = [];
5887         },
5888
5889         fire : function(){
5890             var ls = this.listeners, scope, len = ls.length;
5891             if(len > 0){
5892                 this.firing = true;
5893                 var args = Array.prototype.slice.call(arguments, 0);
5894                 for(var i = 0; i < len; i++){
5895                     var l = ls[i];
5896                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5897                         this.firing = false;
5898                         return false;
5899                     }
5900                 }
5901                 this.firing = false;
5902             }
5903             return true;
5904         }
5905     };
5906 })();/*
5907  * Based on:
5908  * Ext JS Library 1.1.1
5909  * Copyright(c) 2006-2007, Ext JS, LLC.
5910  *
5911  * Originally Released Under LGPL - original licence link has changed is not relivant.
5912  *
5913  * Fork - LGPL
5914  * <script type="text/javascript">
5915  */
5916
5917 /**
5918  * @class Roo.EventManager
5919  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5920  * several useful events directly.
5921  * See {@link Roo.EventObject} for more details on normalized event objects.
5922  * @singleton
5923  */
5924 Roo.EventManager = function(){
5925     var docReadyEvent, docReadyProcId, docReadyState = false;
5926     var resizeEvent, resizeTask, textEvent, textSize;
5927     var E = Roo.lib.Event;
5928     var D = Roo.lib.Dom;
5929
5930
5931     var fireDocReady = function(){
5932         if(!docReadyState){
5933             docReadyState = true;
5934             Roo.isReady = true;
5935             if(docReadyProcId){
5936                 clearInterval(docReadyProcId);
5937             }
5938             if(Roo.isGecko || Roo.isOpera) {
5939                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5940             }
5941             if(Roo.isIE){
5942                 var defer = document.getElementById("ie-deferred-loader");
5943                 if(defer){
5944                     defer.onreadystatechange = null;
5945                     defer.parentNode.removeChild(defer);
5946                 }
5947             }
5948             if(docReadyEvent){
5949                 docReadyEvent.fire();
5950                 docReadyEvent.clearListeners();
5951             }
5952         }
5953     };
5954     
5955     var initDocReady = function(){
5956         docReadyEvent = new Roo.util.Event();
5957         if(Roo.isGecko || Roo.isOpera) {
5958             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5959         }else if(Roo.isIE){
5960             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5961             var defer = document.getElementById("ie-deferred-loader");
5962             defer.onreadystatechange = function(){
5963                 if(this.readyState == "complete"){
5964                     fireDocReady();
5965                 }
5966             };
5967         }else if(Roo.isSafari){ 
5968             docReadyProcId = setInterval(function(){
5969                 var rs = document.readyState;
5970                 if(rs == "complete") {
5971                     fireDocReady();     
5972                  }
5973             }, 10);
5974         }
5975         // no matter what, make sure it fires on load
5976         E.on(window, "load", fireDocReady);
5977     };
5978
5979     var createBuffered = function(h, o){
5980         var task = new Roo.util.DelayedTask(h);
5981         return function(e){
5982             // create new event object impl so new events don't wipe out properties
5983             e = new Roo.EventObjectImpl(e);
5984             task.delay(o.buffer, h, null, [e]);
5985         };
5986     };
5987
5988     var createSingle = function(h, el, ename, fn){
5989         return function(e){
5990             Roo.EventManager.removeListener(el, ename, fn);
5991             h(e);
5992         };
5993     };
5994
5995     var createDelayed = function(h, o){
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             setTimeout(function(){
6000                 h(e);
6001             }, o.delay || 10);
6002         };
6003     };
6004
6005     var listen = function(element, ename, opt, fn, scope){
6006         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6007         fn = fn || o.fn; scope = scope || o.scope;
6008         var el = Roo.getDom(element);
6009         if(!el){
6010             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6011         }
6012         var h = function(e){
6013             e = Roo.EventObject.setEvent(e);
6014             var t;
6015             if(o.delegate){
6016                 t = e.getTarget(o.delegate, el);
6017                 if(!t){
6018                     return;
6019                 }
6020             }else{
6021                 t = e.target;
6022             }
6023             if(o.stopEvent === true){
6024                 e.stopEvent();
6025             }
6026             if(o.preventDefault === true){
6027                e.preventDefault();
6028             }
6029             if(o.stopPropagation === true){
6030                 e.stopPropagation();
6031             }
6032
6033             if(o.normalized === false){
6034                 e = e.browserEvent;
6035             }
6036
6037             fn.call(scope || el, e, t, o);
6038         };
6039         if(o.delay){
6040             h = createDelayed(h, o);
6041         }
6042         if(o.single){
6043             h = createSingle(h, el, ename, fn);
6044         }
6045         if(o.buffer){
6046             h = createBuffered(h, o);
6047         }
6048         fn._handlers = fn._handlers || [];
6049         fn._handlers.push([Roo.id(el), ename, h]);
6050
6051         E.on(el, ename, h);
6052         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6053             el.addEventListener("DOMMouseScroll", h, false);
6054             E.on(window, 'unload', function(){
6055                 el.removeEventListener("DOMMouseScroll", h, false);
6056             });
6057         }
6058         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6059             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6060         }
6061         return h;
6062     };
6063
6064     var stopListening = function(el, ename, fn){
6065         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6066         if(hds){
6067             for(var i = 0, len = hds.length; i < len; i++){
6068                 var h = hds[i];
6069                 if(h[0] == id && h[1] == ename){
6070                     hd = h[2];
6071                     hds.splice(i, 1);
6072                     break;
6073                 }
6074             }
6075         }
6076         E.un(el, ename, hd);
6077         el = Roo.getDom(el);
6078         if(ename == "mousewheel" && el.addEventListener){
6079             el.removeEventListener("DOMMouseScroll", hd, false);
6080         }
6081         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6082             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6083         }
6084     };
6085
6086     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6087     
6088     var pub = {
6089         
6090         
6091         /** 
6092          * Fix for doc tools
6093          * @scope Roo.EventManager
6094          */
6095         
6096         
6097         /** 
6098          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6099          * object with a Roo.EventObject
6100          * @param {Function} fn        The method the event invokes
6101          * @param {Object}   scope    An object that becomes the scope of the handler
6102          * @param {boolean}  override If true, the obj passed in becomes
6103          *                             the execution scope of the listener
6104          * @return {Function} The wrapped function
6105          * @deprecated
6106          */
6107         wrap : function(fn, scope, override){
6108             return function(e){
6109                 Roo.EventObject.setEvent(e);
6110                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6111             };
6112         },
6113         
6114         /**
6115      * Appends an event handler to an element (shorthand for addListener)
6116      * @param {String/HTMLElement}   element        The html element or id to assign the
6117      * @param {String}   eventName The type of event to listen for
6118      * @param {Function} handler The method the event invokes
6119      * @param {Object}   scope (optional) The scope in which to execute the handler
6120      * function. The handler function's "this" context.
6121      * @param {Object}   options (optional) An object containing handler configuration
6122      * properties. This may contain any of the following properties:<ul>
6123      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6124      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6125      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6126      * <li>preventDefault {Boolean} True to prevent the default action</li>
6127      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6128      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6129      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6130      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6131      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6132      * by the specified number of milliseconds. If the event fires again within that time, the original
6133      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6134      * </ul><br>
6135      * <p>
6136      * <b>Combining Options</b><br>
6137      * Using the options argument, it is possible to combine different types of listeners:<br>
6138      * <br>
6139      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6140      * Code:<pre><code>
6141 el.on('click', this.onClick, this, {
6142     single: true,
6143     delay: 100,
6144     stopEvent : true,
6145     forumId: 4
6146 });</code></pre>
6147      * <p>
6148      * <b>Attaching multiple handlers in 1 call</b><br>
6149       * The method also allows for a single argument to be passed which is a config object containing properties
6150      * which specify multiple handlers.
6151      * <p>
6152      * Code:<pre><code>
6153 el.on({
6154     'click' : {
6155         fn: this.onClick
6156         scope: this,
6157         delay: 100
6158     },
6159     'mouseover' : {
6160         fn: this.onMouseOver
6161         scope: this
6162     },
6163     'mouseout' : {
6164         fn: this.onMouseOut
6165         scope: this
6166     }
6167 });</code></pre>
6168      * <p>
6169      * Or a shorthand syntax:<br>
6170      * Code:<pre><code>
6171 el.on({
6172     'click' : this.onClick,
6173     'mouseover' : this.onMouseOver,
6174     'mouseout' : this.onMouseOut
6175     scope: this
6176 });</code></pre>
6177      */
6178         addListener : function(element, eventName, fn, scope, options){
6179             if(typeof eventName == "object"){
6180                 var o = eventName;
6181                 for(var e in o){
6182                     if(propRe.test(e)){
6183                         continue;
6184                     }
6185                     if(typeof o[e] == "function"){
6186                         // shared options
6187                         listen(element, e, o, o[e], o.scope);
6188                     }else{
6189                         // individual options
6190                         listen(element, e, o[e]);
6191                     }
6192                 }
6193                 return;
6194             }
6195             return listen(element, eventName, options, fn, scope);
6196         },
6197         
6198         /**
6199          * Removes an event handler
6200          *
6201          * @param {String/HTMLElement}   element        The id or html element to remove the 
6202          *                             event from
6203          * @param {String}   eventName     The type of event
6204          * @param {Function} fn
6205          * @return {Boolean} True if a listener was actually removed
6206          */
6207         removeListener : function(element, eventName, fn){
6208             return stopListening(element, eventName, fn);
6209         },
6210         
6211         /**
6212          * Fires when the document is ready (before onload and before images are loaded). Can be 
6213          * accessed shorthanded Roo.onReady().
6214          * @param {Function} fn        The method the event invokes
6215          * @param {Object}   scope    An  object that becomes the scope of the handler
6216          * @param {boolean}  options
6217          */
6218         onDocumentReady : function(fn, scope, options){
6219             if(docReadyState){ // if it already fired
6220                 docReadyEvent.addListener(fn, scope, options);
6221                 docReadyEvent.fire();
6222                 docReadyEvent.clearListeners();
6223                 return;
6224             }
6225             if(!docReadyEvent){
6226                 initDocReady();
6227             }
6228             docReadyEvent.addListener(fn, scope, options);
6229         },
6230         
6231         /**
6232          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6233          * @param {Function} fn        The method the event invokes
6234          * @param {Object}   scope    An object that becomes the scope of the handler
6235          * @param {boolean}  options
6236          */
6237         onWindowResize : function(fn, scope, options){
6238             if(!resizeEvent){
6239                 resizeEvent = new Roo.util.Event();
6240                 resizeTask = new Roo.util.DelayedTask(function(){
6241                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6242                 });
6243                 E.on(window, "resize", function(){
6244                     if(Roo.isIE){
6245                         resizeTask.delay(50);
6246                     }else{
6247                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6248                     }
6249                 });
6250             }
6251             resizeEvent.addListener(fn, scope, options);
6252         },
6253
6254         /**
6255          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  options
6259          */
6260         onTextResize : function(fn, scope, options){
6261             if(!textEvent){
6262                 textEvent = new Roo.util.Event();
6263                 var textEl = new Roo.Element(document.createElement('div'));
6264                 textEl.dom.className = 'x-text-resize';
6265                 textEl.dom.innerHTML = 'X';
6266                 textEl.appendTo(document.body);
6267                 textSize = textEl.dom.offsetHeight;
6268                 setInterval(function(){
6269                     if(textEl.dom.offsetHeight != textSize){
6270                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6271                     }
6272                 }, this.textResizeInterval);
6273             }
6274             textEvent.addListener(fn, scope, options);
6275         },
6276
6277         /**
6278          * Removes the passed window resize listener.
6279          * @param {Function} fn        The method the event invokes
6280          * @param {Object}   scope    The scope of handler
6281          */
6282         removeResizeListener : function(fn, scope){
6283             if(resizeEvent){
6284                 resizeEvent.removeListener(fn, scope);
6285             }
6286         },
6287
6288         // private
6289         fireResize : function(){
6290             if(resizeEvent){
6291                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6292             }   
6293         },
6294         /**
6295          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6296          */
6297         ieDeferSrc : false,
6298         /**
6299          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6300          */
6301         textResizeInterval : 50
6302     };
6303     
6304     /**
6305      * Fix for doc tools
6306      * @scopeAlias pub=Roo.EventManager
6307      */
6308     
6309      /**
6310      * Appends an event handler to an element (shorthand for addListener)
6311      * @param {String/HTMLElement}   element        The html element or id to assign the
6312      * @param {String}   eventName The type of event to listen for
6313      * @param {Function} handler The method the event invokes
6314      * @param {Object}   scope (optional) The scope in which to execute the handler
6315      * function. The handler function's "this" context.
6316      * @param {Object}   options (optional) An object containing handler configuration
6317      * properties. This may contain any of the following properties:<ul>
6318      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6319      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6320      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6321      * <li>preventDefault {Boolean} True to prevent the default action</li>
6322      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6323      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6324      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6325      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6326      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6327      * by the specified number of milliseconds. If the event fires again within that time, the original
6328      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6329      * </ul><br>
6330      * <p>
6331      * <b>Combining Options</b><br>
6332      * Using the options argument, it is possible to combine different types of listeners:<br>
6333      * <br>
6334      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6335      * Code:<pre><code>
6336 el.on('click', this.onClick, this, {
6337     single: true,
6338     delay: 100,
6339     stopEvent : true,
6340     forumId: 4
6341 });</code></pre>
6342      * <p>
6343      * <b>Attaching multiple handlers in 1 call</b><br>
6344       * The method also allows for a single argument to be passed which is a config object containing properties
6345      * which specify multiple handlers.
6346      * <p>
6347      * Code:<pre><code>
6348 el.on({
6349     'click' : {
6350         fn: this.onClick
6351         scope: this,
6352         delay: 100
6353     },
6354     'mouseover' : {
6355         fn: this.onMouseOver
6356         scope: this
6357     },
6358     'mouseout' : {
6359         fn: this.onMouseOut
6360         scope: this
6361     }
6362 });</code></pre>
6363      * <p>
6364      * Or a shorthand syntax:<br>
6365      * Code:<pre><code>
6366 el.on({
6367     'click' : this.onClick,
6368     'mouseover' : this.onMouseOver,
6369     'mouseout' : this.onMouseOut
6370     scope: this
6371 });</code></pre>
6372      */
6373     pub.on = pub.addListener;
6374     pub.un = pub.removeListener;
6375
6376     pub.stoppedMouseDownEvent = new Roo.util.Event();
6377     return pub;
6378 }();
6379 /**
6380   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6381   * @param {Function} fn        The method the event invokes
6382   * @param {Object}   scope    An  object that becomes the scope of the handler
6383   * @param {boolean}  override If true, the obj passed in becomes
6384   *                             the execution scope of the listener
6385   * @member Roo
6386   * @method onReady
6387  */
6388 Roo.onReady = Roo.EventManager.onDocumentReady;
6389
6390 Roo.onReady(function(){
6391     var bd = Roo.get(document.body);
6392     if(!bd){ return; }
6393
6394     var cls = [
6395             Roo.isIE ? "roo-ie"
6396             : Roo.isGecko ? "roo-gecko"
6397             : Roo.isOpera ? "roo-opera"
6398             : Roo.isSafari ? "roo-safari" : ""];
6399
6400     if(Roo.isMac){
6401         cls.push("roo-mac");
6402     }
6403     if(Roo.isLinux){
6404         cls.push("roo-linux");
6405     }
6406     if(Roo.isBorderBox){
6407         cls.push('roo-border-box');
6408     }
6409     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6410         var p = bd.dom.parentNode;
6411         if(p){
6412             p.className += ' roo-strict';
6413         }
6414     }
6415     bd.addClass(cls.join(' '));
6416 });
6417
6418 /**
6419  * @class Roo.EventObject
6420  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6421  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6422  * Example:
6423  * <pre><code>
6424  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6425     e.preventDefault();
6426     var target = e.getTarget();
6427     ...
6428  }
6429  var myDiv = Roo.get("myDiv");
6430  myDiv.on("click", handleClick);
6431  //or
6432  Roo.EventManager.on("myDiv", 'click', handleClick);
6433  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6434  </code></pre>
6435  * @singleton
6436  */
6437 Roo.EventObject = function(){
6438     
6439     var E = Roo.lib.Event;
6440     
6441     // safari keypress events for special keys return bad keycodes
6442     var safariKeys = {
6443         63234 : 37, // left
6444         63235 : 39, // right
6445         63232 : 38, // up
6446         63233 : 40, // down
6447         63276 : 33, // page up
6448         63277 : 34, // page down
6449         63272 : 46, // delete
6450         63273 : 36, // home
6451         63275 : 35  // end
6452     };
6453
6454     // normalize button clicks
6455     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6456                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6457
6458     Roo.EventObjectImpl = function(e){
6459         if(e){
6460             this.setEvent(e.browserEvent || e);
6461         }
6462     };
6463     Roo.EventObjectImpl.prototype = {
6464         /**
6465          * Used to fix doc tools.
6466          * @scope Roo.EventObject.prototype
6467          */
6468             
6469
6470         
6471         
6472         /** The normal browser event */
6473         browserEvent : null,
6474         /** The button pressed in a mouse event */
6475         button : -1,
6476         /** True if the shift key was down during the event */
6477         shiftKey : false,
6478         /** True if the control key was down during the event */
6479         ctrlKey : false,
6480         /** True if the alt key was down during the event */
6481         altKey : false,
6482
6483         /** Key constant 
6484         * @type Number */
6485         BACKSPACE : 8,
6486         /** Key constant 
6487         * @type Number */
6488         TAB : 9,
6489         /** Key constant 
6490         * @type Number */
6491         RETURN : 13,
6492         /** Key constant 
6493         * @type Number */
6494         ENTER : 13,
6495         /** Key constant 
6496         * @type Number */
6497         SHIFT : 16,
6498         /** Key constant 
6499         * @type Number */
6500         CONTROL : 17,
6501         /** Key constant 
6502         * @type Number */
6503         ESC : 27,
6504         /** Key constant 
6505         * @type Number */
6506         SPACE : 32,
6507         /** Key constant 
6508         * @type Number */
6509         PAGEUP : 33,
6510         /** Key constant 
6511         * @type Number */
6512         PAGEDOWN : 34,
6513         /** Key constant 
6514         * @type Number */
6515         END : 35,
6516         /** Key constant 
6517         * @type Number */
6518         HOME : 36,
6519         /** Key constant 
6520         * @type Number */
6521         LEFT : 37,
6522         /** Key constant 
6523         * @type Number */
6524         UP : 38,
6525         /** Key constant 
6526         * @type Number */
6527         RIGHT : 39,
6528         /** Key constant 
6529         * @type Number */
6530         DOWN : 40,
6531         /** Key constant 
6532         * @type Number */
6533         DELETE : 46,
6534         /** Key constant 
6535         * @type Number */
6536         F5 : 116,
6537
6538            /** @private */
6539         setEvent : function(e){
6540             if(e == this || (e && e.browserEvent)){ // already wrapped
6541                 return e;
6542             }
6543             this.browserEvent = e;
6544             if(e){
6545                 // normalize buttons
6546                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6547                 if(e.type == 'click' && this.button == -1){
6548                     this.button = 0;
6549                 }
6550                 this.type = e.type;
6551                 this.shiftKey = e.shiftKey;
6552                 // mac metaKey behaves like ctrlKey
6553                 this.ctrlKey = e.ctrlKey || e.metaKey;
6554                 this.altKey = e.altKey;
6555                 // in getKey these will be normalized for the mac
6556                 this.keyCode = e.keyCode;
6557                 // keyup warnings on firefox.
6558                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6559                 // cache the target for the delayed and or buffered events
6560                 this.target = E.getTarget(e);
6561                 // same for XY
6562                 this.xy = E.getXY(e);
6563             }else{
6564                 this.button = -1;
6565                 this.shiftKey = false;
6566                 this.ctrlKey = false;
6567                 this.altKey = false;
6568                 this.keyCode = 0;
6569                 this.charCode =0;
6570                 this.target = null;
6571                 this.xy = [0, 0];
6572             }
6573             return this;
6574         },
6575
6576         /**
6577          * Stop the event (preventDefault and stopPropagation)
6578          */
6579         stopEvent : function(){
6580             if(this.browserEvent){
6581                 if(this.browserEvent.type == 'mousedown'){
6582                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6583                 }
6584                 E.stopEvent(this.browserEvent);
6585             }
6586         },
6587
6588         /**
6589          * Prevents the browsers default handling of the event.
6590          */
6591         preventDefault : function(){
6592             if(this.browserEvent){
6593                 E.preventDefault(this.browserEvent);
6594             }
6595         },
6596
6597         /** @private */
6598         isNavKeyPress : function(){
6599             var k = this.keyCode;
6600             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6601             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6602         },
6603
6604         isSpecialKey : function(){
6605             var k = this.keyCode;
6606             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6607             (k == 16) || (k == 17) ||
6608             (k >= 18 && k <= 20) ||
6609             (k >= 33 && k <= 35) ||
6610             (k >= 36 && k <= 39) ||
6611             (k >= 44 && k <= 45);
6612         },
6613         /**
6614          * Cancels bubbling of the event.
6615          */
6616         stopPropagation : function(){
6617             if(this.browserEvent){
6618                 if(this.type == 'mousedown'){
6619                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6620                 }
6621                 E.stopPropagation(this.browserEvent);
6622             }
6623         },
6624
6625         /**
6626          * Gets the key code for the event.
6627          * @return {Number}
6628          */
6629         getCharCode : function(){
6630             return this.charCode || this.keyCode;
6631         },
6632
6633         /**
6634          * Returns a normalized keyCode for the event.
6635          * @return {Number} The key code
6636          */
6637         getKey : function(){
6638             var k = this.keyCode || this.charCode;
6639             return Roo.isSafari ? (safariKeys[k] || k) : k;
6640         },
6641
6642         /**
6643          * Gets the x coordinate of the event.
6644          * @return {Number}
6645          */
6646         getPageX : function(){
6647             return this.xy[0];
6648         },
6649
6650         /**
6651          * Gets the y coordinate of the event.
6652          * @return {Number}
6653          */
6654         getPageY : function(){
6655             return this.xy[1];
6656         },
6657
6658         /**
6659          * Gets the time of the event.
6660          * @return {Number}
6661          */
6662         getTime : function(){
6663             if(this.browserEvent){
6664                 return E.getTime(this.browserEvent);
6665             }
6666             return null;
6667         },
6668
6669         /**
6670          * Gets the page coordinates of the event.
6671          * @return {Array} The xy values like [x, y]
6672          */
6673         getXY : function(){
6674             return this.xy;
6675         },
6676
6677         /**
6678          * Gets the target for the event.
6679          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6680          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6681                 search as a number or element (defaults to 10 || document.body)
6682          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6683          * @return {HTMLelement}
6684          */
6685         getTarget : function(selector, maxDepth, returnEl){
6686             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6687         },
6688         /**
6689          * Gets the related target.
6690          * @return {HTMLElement}
6691          */
6692         getRelatedTarget : function(){
6693             if(this.browserEvent){
6694                 return E.getRelatedTarget(this.browserEvent);
6695             }
6696             return null;
6697         },
6698
6699         /**
6700          * Normalizes mouse wheel delta across browsers
6701          * @return {Number} The delta
6702          */
6703         getWheelDelta : function(){
6704             var e = this.browserEvent;
6705             var delta = 0;
6706             if(e.wheelDelta){ /* IE/Opera. */
6707                 delta = e.wheelDelta/120;
6708             }else if(e.detail){ /* Mozilla case. */
6709                 delta = -e.detail/3;
6710             }
6711             return delta;
6712         },
6713
6714         /**
6715          * Returns true if the control, meta, shift or alt key was pressed during this event.
6716          * @return {Boolean}
6717          */
6718         hasModifier : function(){
6719             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6720         },
6721
6722         /**
6723          * Returns true if the target of this event equals el or is a child of el
6724          * @param {String/HTMLElement/Element} el
6725          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6726          * @return {Boolean}
6727          */
6728         within : function(el, related){
6729             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6730             return t && Roo.fly(el).contains(t);
6731         },
6732
6733         getPoint : function(){
6734             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6735         }
6736     };
6737
6738     return new Roo.EventObjectImpl();
6739 }();
6740             
6741     /*
6742  * Based on:
6743  * Ext JS Library 1.1.1
6744  * Copyright(c) 2006-2007, Ext JS, LLC.
6745  *
6746  * Originally Released Under LGPL - original licence link has changed is not relivant.
6747  *
6748  * Fork - LGPL
6749  * <script type="text/javascript">
6750  */
6751
6752  
6753 // was in Composite Element!??!?!
6754  
6755 (function(){
6756     var D = Roo.lib.Dom;
6757     var E = Roo.lib.Event;
6758     var A = Roo.lib.Anim;
6759
6760     // local style camelizing for speed
6761     var propCache = {};
6762     var camelRe = /(-[a-z])/gi;
6763     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6764     var view = document.defaultView;
6765
6766 /**
6767  * @class Roo.Element
6768  * Represents an Element in the DOM.<br><br>
6769  * Usage:<br>
6770 <pre><code>
6771 var el = Roo.get("my-div");
6772
6773 // or with getEl
6774 var el = getEl("my-div");
6775
6776 // or with a DOM element
6777 var el = Roo.get(myDivElement);
6778 </code></pre>
6779  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6780  * each call instead of constructing a new one.<br><br>
6781  * <b>Animations</b><br />
6782  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6783  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6784 <pre>
6785 Option    Default   Description
6786 --------- --------  ---------------------------------------------
6787 duration  .35       The duration of the animation in seconds
6788 easing    easeOut   The YUI easing method
6789 callback  none      A function to execute when the anim completes
6790 scope     this      The scope (this) of the callback function
6791 </pre>
6792 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6793 * manipulate the animation. Here's an example:
6794 <pre><code>
6795 var el = Roo.get("my-div");
6796
6797 // no animation
6798 el.setWidth(100);
6799
6800 // default animation
6801 el.setWidth(100, true);
6802
6803 // animation with some options set
6804 el.setWidth(100, {
6805     duration: 1,
6806     callback: this.foo,
6807     scope: this
6808 });
6809
6810 // using the "anim" property to get the Anim object
6811 var opt = {
6812     duration: 1,
6813     callback: this.foo,
6814     scope: this
6815 };
6816 el.setWidth(100, opt);
6817 ...
6818 if(opt.anim.isAnimated()){
6819     opt.anim.stop();
6820 }
6821 </code></pre>
6822 * <b> Composite (Collections of) Elements</b><br />
6823  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6824  * @constructor Create a new Element directly.
6825  * @param {String/HTMLElement} element
6826  * @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).
6827  */
6828     Roo.Element = function(element, forceNew){
6829         var dom = typeof element == "string" ?
6830                 document.getElementById(element) : element;
6831         if(!dom){ // invalid id/element
6832             return null;
6833         }
6834         var id = dom.id;
6835         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6836             return Roo.Element.cache[id];
6837         }
6838
6839         /**
6840          * The DOM element
6841          * @type HTMLElement
6842          */
6843         this.dom = dom;
6844
6845         /**
6846          * The DOM element ID
6847          * @type String
6848          */
6849         this.id = id || Roo.id(dom);
6850     };
6851
6852     var El = Roo.Element;
6853
6854     El.prototype = {
6855         /**
6856          * The element's default display mode  (defaults to "")
6857          * @type String
6858          */
6859         originalDisplay : "",
6860
6861         visibilityMode : 1,
6862         /**
6863          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6864          * @type String
6865          */
6866         defaultUnit : "px",
6867         /**
6868          * Sets the element's visibility mode. When setVisible() is called it
6869          * will use this to determine whether to set the visibility or the display property.
6870          * @param visMode Element.VISIBILITY or Element.DISPLAY
6871          * @return {Roo.Element} this
6872          */
6873         setVisibilityMode : function(visMode){
6874             this.visibilityMode = visMode;
6875             return this;
6876         },
6877         /**
6878          * Convenience method for setVisibilityMode(Element.DISPLAY)
6879          * @param {String} display (optional) What to set display to when visible
6880          * @return {Roo.Element} this
6881          */
6882         enableDisplayMode : function(display){
6883             this.setVisibilityMode(El.DISPLAY);
6884             if(typeof display != "undefined") this.originalDisplay = display;
6885             return this;
6886         },
6887
6888         /**
6889          * 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)
6890          * @param {String} selector The simple selector to test
6891          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6892                 search as a number or element (defaults to 10 || document.body)
6893          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6894          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6895          */
6896         findParent : function(simpleSelector, maxDepth, returnEl){
6897             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6898             maxDepth = maxDepth || 50;
6899             if(typeof maxDepth != "number"){
6900                 stopEl = Roo.getDom(maxDepth);
6901                 maxDepth = 10;
6902             }
6903             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6904                 if(dq.is(p, simpleSelector)){
6905                     return returnEl ? Roo.get(p) : p;
6906                 }
6907                 depth++;
6908                 p = p.parentNode;
6909             }
6910             return null;
6911         },
6912
6913
6914         /**
6915          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6916          * @param {String} selector The simple selector to test
6917          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6918                 search as a number or element (defaults to 10 || document.body)
6919          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6920          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6921          */
6922         findParentNode : function(simpleSelector, maxDepth, returnEl){
6923             var p = Roo.fly(this.dom.parentNode, '_internal');
6924             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6925         },
6926
6927         /**
6928          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6929          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6930          * @param {String} selector The simple selector to test
6931          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6932                 search as a number or element (defaults to 10 || document.body)
6933          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6934          */
6935         up : function(simpleSelector, maxDepth){
6936             return this.findParentNode(simpleSelector, maxDepth, true);
6937         },
6938
6939
6940
6941         /**
6942          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6943          * @param {String} selector The simple selector to test
6944          * @return {Boolean} True if this element matches the selector, else false
6945          */
6946         is : function(simpleSelector){
6947             return Roo.DomQuery.is(this.dom, simpleSelector);
6948         },
6949
6950         /**
6951          * Perform animation on this element.
6952          * @param {Object} args The YUI animation control args
6953          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6954          * @param {Function} onComplete (optional) Function to call when animation completes
6955          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6956          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6957          * @return {Roo.Element} this
6958          */
6959         animate : function(args, duration, onComplete, easing, animType){
6960             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6961             return this;
6962         },
6963
6964         /*
6965          * @private Internal animation call
6966          */
6967         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6968             animType = animType || 'run';
6969             opt = opt || {};
6970             var anim = Roo.lib.Anim[animType](
6971                 this.dom, args,
6972                 (opt.duration || defaultDur) || .35,
6973                 (opt.easing || defaultEase) || 'easeOut',
6974                 function(){
6975                     Roo.callback(cb, this);
6976                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6977                 },
6978                 this
6979             );
6980             opt.anim = anim;
6981             return anim;
6982         },
6983
6984         // private legacy anim prep
6985         preanim : function(a, i){
6986             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6987         },
6988
6989         /**
6990          * Removes worthless text nodes
6991          * @param {Boolean} forceReclean (optional) By default the element
6992          * keeps track if it has been cleaned already so
6993          * you can call this over and over. However, if you update the element and
6994          * need to force a reclean, you can pass true.
6995          */
6996         clean : function(forceReclean){
6997             if(this.isCleaned && forceReclean !== true){
6998                 return this;
6999             }
7000             var ns = /\S/;
7001             var d = this.dom, n = d.firstChild, ni = -1;
7002             while(n){
7003                 var nx = n.nextSibling;
7004                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7005                     d.removeChild(n);
7006                 }else{
7007                     n.nodeIndex = ++ni;
7008                 }
7009                 n = nx;
7010             }
7011             this.isCleaned = true;
7012             return this;
7013         },
7014
7015         // private
7016         calcOffsetsTo : function(el){
7017             el = Roo.get(el);
7018             var d = el.dom;
7019             var restorePos = false;
7020             if(el.getStyle('position') == 'static'){
7021                 el.position('relative');
7022                 restorePos = true;
7023             }
7024             var x = 0, y =0;
7025             var op = this.dom;
7026             while(op && op != d && op.tagName != 'HTML'){
7027                 x+= op.offsetLeft;
7028                 y+= op.offsetTop;
7029                 op = op.offsetParent;
7030             }
7031             if(restorePos){
7032                 el.position('static');
7033             }
7034             return [x, y];
7035         },
7036
7037         /**
7038          * Scrolls this element into view within the passed container.
7039          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7040          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7041          * @return {Roo.Element} this
7042          */
7043         scrollIntoView : function(container, hscroll){
7044             var c = Roo.getDom(container) || document.body;
7045             var el = this.dom;
7046
7047             var o = this.calcOffsetsTo(c),
7048                 l = o[0],
7049                 t = o[1],
7050                 b = t+el.offsetHeight,
7051                 r = l+el.offsetWidth;
7052
7053             var ch = c.clientHeight;
7054             var ct = parseInt(c.scrollTop, 10);
7055             var cl = parseInt(c.scrollLeft, 10);
7056             var cb = ct + ch;
7057             var cr = cl + c.clientWidth;
7058
7059             if(t < ct){
7060                 c.scrollTop = t;
7061             }else if(b > cb){
7062                 c.scrollTop = b-ch;
7063             }
7064
7065             if(hscroll !== false){
7066                 if(l < cl){
7067                     c.scrollLeft = l;
7068                 }else if(r > cr){
7069                     c.scrollLeft = r-c.clientWidth;
7070                 }
7071             }
7072             return this;
7073         },
7074
7075         // private
7076         scrollChildIntoView : function(child, hscroll){
7077             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7078         },
7079
7080         /**
7081          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7082          * the new height may not be available immediately.
7083          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7084          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7085          * @param {Function} onComplete (optional) Function to call when animation completes
7086          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7087          * @return {Roo.Element} this
7088          */
7089         autoHeight : function(animate, duration, onComplete, easing){
7090             var oldHeight = this.getHeight();
7091             this.clip();
7092             this.setHeight(1); // force clipping
7093             setTimeout(function(){
7094                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7095                 if(!animate){
7096                     this.setHeight(height);
7097                     this.unclip();
7098                     if(typeof onComplete == "function"){
7099                         onComplete();
7100                     }
7101                 }else{
7102                     this.setHeight(oldHeight); // restore original height
7103                     this.setHeight(height, animate, duration, function(){
7104                         this.unclip();
7105                         if(typeof onComplete == "function") onComplete();
7106                     }.createDelegate(this), easing);
7107                 }
7108             }.createDelegate(this), 0);
7109             return this;
7110         },
7111
7112         /**
7113          * Returns true if this element is an ancestor of the passed element
7114          * @param {HTMLElement/String} el The element to check
7115          * @return {Boolean} True if this element is an ancestor of el, else false
7116          */
7117         contains : function(el){
7118             if(!el){return false;}
7119             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7120         },
7121
7122         /**
7123          * Checks whether the element is currently visible using both visibility and display properties.
7124          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7125          * @return {Boolean} True if the element is currently visible, else false
7126          */
7127         isVisible : function(deep) {
7128             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7129             if(deep !== true || !vis){
7130                 return vis;
7131             }
7132             var p = this.dom.parentNode;
7133             while(p && p.tagName.toLowerCase() != "body"){
7134                 if(!Roo.fly(p, '_isVisible').isVisible()){
7135                     return false;
7136                 }
7137                 p = p.parentNode;
7138             }
7139             return true;
7140         },
7141
7142         /**
7143          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7144          * @param {String} selector The CSS selector
7145          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7146          * @return {CompositeElement/CompositeElementLite} The composite element
7147          */
7148         select : function(selector, unique){
7149             return El.select(selector, unique, this.dom);
7150         },
7151
7152         /**
7153          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7154          * @param {String} selector The CSS selector
7155          * @return {Array} An array of the matched nodes
7156          */
7157         query : function(selector, unique){
7158             return Roo.DomQuery.select(selector, this.dom);
7159         },
7160
7161         /**
7162          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7163          * @param {String} selector The CSS selector
7164          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7165          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7166          */
7167         child : function(selector, returnDom){
7168             var n = Roo.DomQuery.selectNode(selector, this.dom);
7169             return returnDom ? n : Roo.get(n);
7170         },
7171
7172         /**
7173          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7174          * @param {String} selector The CSS selector
7175          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7176          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7177          */
7178         down : function(selector, returnDom){
7179             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7180             return returnDom ? n : Roo.get(n);
7181         },
7182
7183         /**
7184          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7185          * @param {String} group The group the DD object is member of
7186          * @param {Object} config The DD config object
7187          * @param {Object} overrides An object containing methods to override/implement on the DD object
7188          * @return {Roo.dd.DD} The DD object
7189          */
7190         initDD : function(group, config, overrides){
7191             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7192             return Roo.apply(dd, overrides);
7193         },
7194
7195         /**
7196          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7197          * @param {String} group The group the DDProxy object is member of
7198          * @param {Object} config The DDProxy config object
7199          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7200          * @return {Roo.dd.DDProxy} The DDProxy object
7201          */
7202         initDDProxy : function(group, config, overrides){
7203             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7204             return Roo.apply(dd, overrides);
7205         },
7206
7207         /**
7208          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7209          * @param {String} group The group the DDTarget object is member of
7210          * @param {Object} config The DDTarget config object
7211          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7212          * @return {Roo.dd.DDTarget} The DDTarget object
7213          */
7214         initDDTarget : function(group, config, overrides){
7215             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7216             return Roo.apply(dd, overrides);
7217         },
7218
7219         /**
7220          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7221          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7222          * @param {Boolean} visible Whether the element is visible
7223          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7224          * @return {Roo.Element} this
7225          */
7226          setVisible : function(visible, animate){
7227             if(!animate || !A){
7228                 if(this.visibilityMode == El.DISPLAY){
7229                     this.setDisplayed(visible);
7230                 }else{
7231                     this.fixDisplay();
7232                     this.dom.style.visibility = visible ? "visible" : "hidden";
7233                 }
7234             }else{
7235                 // closure for composites
7236                 var dom = this.dom;
7237                 var visMode = this.visibilityMode;
7238                 if(visible){
7239                     this.setOpacity(.01);
7240                     this.setVisible(true);
7241                 }
7242                 this.anim({opacity: { to: (visible?1:0) }},
7243                       this.preanim(arguments, 1),
7244                       null, .35, 'easeIn', function(){
7245                          if(!visible){
7246                              if(visMode == El.DISPLAY){
7247                                  dom.style.display = "none";
7248                              }else{
7249                                  dom.style.visibility = "hidden";
7250                              }
7251                              Roo.get(dom).setOpacity(1);
7252                          }
7253                      });
7254             }
7255             return this;
7256         },
7257
7258         /**
7259          * Returns true if display is not "none"
7260          * @return {Boolean}
7261          */
7262         isDisplayed : function() {
7263             return this.getStyle("display") != "none";
7264         },
7265
7266         /**
7267          * Toggles the element's visibility or display, depending on visibility mode.
7268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7269          * @return {Roo.Element} this
7270          */
7271         toggle : function(animate){
7272             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7273             return this;
7274         },
7275
7276         /**
7277          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7278          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7279          * @return {Roo.Element} this
7280          */
7281         setDisplayed : function(value) {
7282             if(typeof value == "boolean"){
7283                value = value ? this.originalDisplay : "none";
7284             }
7285             this.setStyle("display", value);
7286             return this;
7287         },
7288
7289         /**
7290          * Tries to focus the element. Any exceptions are caught and ignored.
7291          * @return {Roo.Element} this
7292          */
7293         focus : function() {
7294             try{
7295                 this.dom.focus();
7296             }catch(e){}
7297             return this;
7298         },
7299
7300         /**
7301          * Tries to blur the element. Any exceptions are caught and ignored.
7302          * @return {Roo.Element} this
7303          */
7304         blur : function() {
7305             try{
7306                 this.dom.blur();
7307             }catch(e){}
7308             return this;
7309         },
7310
7311         /**
7312          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7313          * @param {String/Array} className The CSS class to add, or an array of classes
7314          * @return {Roo.Element} this
7315          */
7316         addClass : function(className){
7317             if(className instanceof Array){
7318                 for(var i = 0, len = className.length; i < len; i++) {
7319                     this.addClass(className[i]);
7320                 }
7321             }else{
7322                 if(className && !this.hasClass(className)){
7323                     this.dom.className = this.dom.className + " " + className;
7324                 }
7325             }
7326             return this;
7327         },
7328
7329         /**
7330          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7331          * @param {String/Array} className The CSS class to add, or an array of classes
7332          * @return {Roo.Element} this
7333          */
7334         radioClass : function(className){
7335             var siblings = this.dom.parentNode.childNodes;
7336             for(var i = 0; i < siblings.length; i++) {
7337                 var s = siblings[i];
7338                 if(s.nodeType == 1){
7339                     Roo.get(s).removeClass(className);
7340                 }
7341             }
7342             this.addClass(className);
7343             return this;
7344         },
7345
7346         /**
7347          * Removes one or more CSS classes from the element.
7348          * @param {String/Array} className The CSS class to remove, or an array of classes
7349          * @return {Roo.Element} this
7350          */
7351         removeClass : function(className){
7352             if(!className || !this.dom.className){
7353                 return this;
7354             }
7355             if(className instanceof Array){
7356                 for(var i = 0, len = className.length; i < len; i++) {
7357                     this.removeClass(className[i]);
7358                 }
7359             }else{
7360                 if(this.hasClass(className)){
7361                     var re = this.classReCache[className];
7362                     if (!re) {
7363                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7364                        this.classReCache[className] = re;
7365                     }
7366                     this.dom.className =
7367                         this.dom.className.replace(re, " ");
7368                 }
7369             }
7370             return this;
7371         },
7372
7373         // private
7374         classReCache: {},
7375
7376         /**
7377          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7378          * @param {String} className The CSS class to toggle
7379          * @return {Roo.Element} this
7380          */
7381         toggleClass : function(className){
7382             if(this.hasClass(className)){
7383                 this.removeClass(className);
7384             }else{
7385                 this.addClass(className);
7386             }
7387             return this;
7388         },
7389
7390         /**
7391          * Checks if the specified CSS class exists on this element's DOM node.
7392          * @param {String} className The CSS class to check for
7393          * @return {Boolean} True if the class exists, else false
7394          */
7395         hasClass : function(className){
7396             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7397         },
7398
7399         /**
7400          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7401          * @param {String} oldClassName The CSS class to replace
7402          * @param {String} newClassName The replacement CSS class
7403          * @return {Roo.Element} this
7404          */
7405         replaceClass : function(oldClassName, newClassName){
7406             this.removeClass(oldClassName);
7407             this.addClass(newClassName);
7408             return this;
7409         },
7410
7411         /**
7412          * Returns an object with properties matching the styles requested.
7413          * For example, el.getStyles('color', 'font-size', 'width') might return
7414          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7415          * @param {String} style1 A style name
7416          * @param {String} style2 A style name
7417          * @param {String} etc.
7418          * @return {Object} The style object
7419          */
7420         getStyles : function(){
7421             var a = arguments, len = a.length, r = {};
7422             for(var i = 0; i < len; i++){
7423                 r[a[i]] = this.getStyle(a[i]);
7424             }
7425             return r;
7426         },
7427
7428         /**
7429          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7430          * @param {String} property The style property whose value is returned.
7431          * @return {String} The current value of the style property for this element.
7432          */
7433         getStyle : function(){
7434             return view && view.getComputedStyle ?
7435                 function(prop){
7436                     var el = this.dom, v, cs, camel;
7437                     if(prop == 'float'){
7438                         prop = "cssFloat";
7439                     }
7440                     if(el.style && (v = el.style[prop])){
7441                         return v;
7442                     }
7443                     if(cs = view.getComputedStyle(el, "")){
7444                         if(!(camel = propCache[prop])){
7445                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7446                         }
7447                         return cs[camel];
7448                     }
7449                     return null;
7450                 } :
7451                 function(prop){
7452                     var el = this.dom, v, cs, camel;
7453                     if(prop == 'opacity'){
7454                         if(typeof el.style.filter == 'string'){
7455                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7456                             if(m){
7457                                 var fv = parseFloat(m[1]);
7458                                 if(!isNaN(fv)){
7459                                     return fv ? fv / 100 : 0;
7460                                 }
7461                             }
7462                         }
7463                         return 1;
7464                     }else if(prop == 'float'){
7465                         prop = "styleFloat";
7466                     }
7467                     if(!(camel = propCache[prop])){
7468                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7469                     }
7470                     if(v = el.style[camel]){
7471                         return v;
7472                     }
7473                     if(cs = el.currentStyle){
7474                         return cs[camel];
7475                     }
7476                     return null;
7477                 };
7478         }(),
7479
7480         /**
7481          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7482          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7483          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7484          * @return {Roo.Element} this
7485          */
7486         setStyle : function(prop, value){
7487             if(typeof prop == "string"){
7488                 
7489                 if (prop == 'float') {
7490                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7491                     return this;
7492                 }
7493                 
7494                 var camel;
7495                 if(!(camel = propCache[prop])){
7496                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7497                 }
7498                 
7499                 if(camel == 'opacity') {
7500                     this.setOpacity(value);
7501                 }else{
7502                     this.dom.style[camel] = value;
7503                 }
7504             }else{
7505                 for(var style in prop){
7506                     if(typeof prop[style] != "function"){
7507                        this.setStyle(style, prop[style]);
7508                     }
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * More flexible version of {@link #setStyle} for setting style properties.
7516          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7517          * a function which returns such a specification.
7518          * @return {Roo.Element} this
7519          */
7520         applyStyles : function(style){
7521             Roo.DomHelper.applyStyles(this.dom, style);
7522             return this;
7523         },
7524
7525         /**
7526           * 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).
7527           * @return {Number} The X position of the element
7528           */
7529         getX : function(){
7530             return D.getX(this.dom);
7531         },
7532
7533         /**
7534           * 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).
7535           * @return {Number} The Y position of the element
7536           */
7537         getY : function(){
7538             return D.getY(this.dom);
7539         },
7540
7541         /**
7542           * 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).
7543           * @return {Array} The XY position of the element
7544           */
7545         getXY : function(){
7546             return D.getXY(this.dom);
7547         },
7548
7549         /**
7550          * 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).
7551          * @param {Number} The X position of the element
7552          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7553          * @return {Roo.Element} this
7554          */
7555         setX : function(x, animate){
7556             if(!animate || !A){
7557                 D.setX(this.dom, x);
7558             }else{
7559                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * 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).
7566          * @param {Number} The Y 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         setY : function(y, animate){
7571             if(!animate || !A){
7572                 D.setY(this.dom, y);
7573             }else{
7574                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7581          * @param {String} left The left CSS property value
7582          * @return {Roo.Element} this
7583          */
7584         setLeft : function(left){
7585             this.setStyle("left", this.addUnits(left));
7586             return this;
7587         },
7588
7589         /**
7590          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7591          * @param {String} top The top CSS property value
7592          * @return {Roo.Element} this
7593          */
7594         setTop : function(top){
7595             this.setStyle("top", this.addUnits(top));
7596             return this;
7597         },
7598
7599         /**
7600          * Sets the element's CSS right style.
7601          * @param {String} right The right CSS property value
7602          * @return {Roo.Element} this
7603          */
7604         setRight : function(right){
7605             this.setStyle("right", this.addUnits(right));
7606             return this;
7607         },
7608
7609         /**
7610          * Sets the element's CSS bottom style.
7611          * @param {String} bottom The bottom CSS property value
7612          * @return {Roo.Element} this
7613          */
7614         setBottom : function(bottom){
7615             this.setStyle("bottom", this.addUnits(bottom));
7616             return this;
7617         },
7618
7619         /**
7620          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7621          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7622          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7623          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7624          * @return {Roo.Element} this
7625          */
7626         setXY : function(pos, animate){
7627             if(!animate || !A){
7628                 D.setXY(this.dom, pos);
7629             }else{
7630                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7637          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7638          * @param {Number} x X value for new position (coordinates are page-based)
7639          * @param {Number} y Y value for new position (coordinates are page-based)
7640          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7641          * @return {Roo.Element} this
7642          */
7643         setLocation : function(x, y, animate){
7644             this.setXY([x, y], this.preanim(arguments, 2));
7645             return this;
7646         },
7647
7648         /**
7649          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7650          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7651          * @param {Number} x X value for new position (coordinates are page-based)
7652          * @param {Number} y Y value for new position (coordinates are page-based)
7653          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7654          * @return {Roo.Element} this
7655          */
7656         moveTo : function(x, y, animate){
7657             this.setXY([x, y], this.preanim(arguments, 2));
7658             return this;
7659         },
7660
7661         /**
7662          * Returns the region of the given element.
7663          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7664          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7665          */
7666         getRegion : function(){
7667             return D.getRegion(this.dom);
7668         },
7669
7670         /**
7671          * Returns the offset height of the element
7672          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7673          * @return {Number} The element's height
7674          */
7675         getHeight : function(contentHeight){
7676             var h = this.dom.offsetHeight || 0;
7677             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7678         },
7679
7680         /**
7681          * Returns the offset width of the element
7682          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7683          * @return {Number} The element's width
7684          */
7685         getWidth : function(contentWidth){
7686             var w = this.dom.offsetWidth || 0;
7687             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7688         },
7689
7690         /**
7691          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7692          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7693          * if a height has not been set using CSS.
7694          * @return {Number}
7695          */
7696         getComputedHeight : function(){
7697             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7698             if(!h){
7699                 h = parseInt(this.getStyle('height'), 10) || 0;
7700                 if(!this.isBorderBox()){
7701                     h += this.getFrameWidth('tb');
7702                 }
7703             }
7704             return h;
7705         },
7706
7707         /**
7708          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7709          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7710          * if a width has not been set using CSS.
7711          * @return {Number}
7712          */
7713         getComputedWidth : function(){
7714             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7715             if(!w){
7716                 w = parseInt(this.getStyle('width'), 10) || 0;
7717                 if(!this.isBorderBox()){
7718                     w += this.getFrameWidth('lr');
7719                 }
7720             }
7721             return w;
7722         },
7723
7724         /**
7725          * Returns the size of the element.
7726          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7727          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7728          */
7729         getSize : function(contentSize){
7730             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7731         },
7732
7733         /**
7734          * Returns the width and height of the viewport.
7735          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7736          */
7737         getViewSize : function(){
7738             var d = this.dom, doc = document, aw = 0, ah = 0;
7739             if(d == doc || d == doc.body){
7740                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7741             }else{
7742                 return {
7743                     width : d.clientWidth,
7744                     height: d.clientHeight
7745                 };
7746             }
7747         },
7748
7749         /**
7750          * Returns the value of the "value" attribute
7751          * @param {Boolean} asNumber true to parse the value as a number
7752          * @return {String/Number}
7753          */
7754         getValue : function(asNumber){
7755             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7756         },
7757
7758         // private
7759         adjustWidth : function(width){
7760             if(typeof width == "number"){
7761                 if(this.autoBoxAdjust && !this.isBorderBox()){
7762                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7763                 }
7764                 if(width < 0){
7765                     width = 0;
7766                 }
7767             }
7768             return width;
7769         },
7770
7771         // private
7772         adjustHeight : function(height){
7773             if(typeof height == "number"){
7774                if(this.autoBoxAdjust && !this.isBorderBox()){
7775                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7776                }
7777                if(height < 0){
7778                    height = 0;
7779                }
7780             }
7781             return height;
7782         },
7783
7784         /**
7785          * Set the width of the element
7786          * @param {Number} width The new width
7787          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7788          * @return {Roo.Element} this
7789          */
7790         setWidth : function(width, animate){
7791             width = this.adjustWidth(width);
7792             if(!animate || !A){
7793                 this.dom.style.width = this.addUnits(width);
7794             }else{
7795                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7796             }
7797             return this;
7798         },
7799
7800         /**
7801          * Set the height of the element
7802          * @param {Number} height The new height
7803          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7804          * @return {Roo.Element} this
7805          */
7806          setHeight : function(height, animate){
7807             height = this.adjustHeight(height);
7808             if(!animate || !A){
7809                 this.dom.style.height = this.addUnits(height);
7810             }else{
7811                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7812             }
7813             return this;
7814         },
7815
7816         /**
7817          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7818          * @param {Number} width The new width
7819          * @param {Number} height The new height
7820          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7821          * @return {Roo.Element} this
7822          */
7823          setSize : function(width, height, animate){
7824             if(typeof width == "object"){ // in case of object from getSize()
7825                 height = width.height; width = width.width;
7826             }
7827             width = this.adjustWidth(width); height = this.adjustHeight(height);
7828             if(!animate || !A){
7829                 this.dom.style.width = this.addUnits(width);
7830                 this.dom.style.height = this.addUnits(height);
7831             }else{
7832                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7833             }
7834             return this;
7835         },
7836
7837         /**
7838          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7839          * @param {Number} x X value for new position (coordinates are page-based)
7840          * @param {Number} y Y value for new position (coordinates are page-based)
7841          * @param {Number} width The new width
7842          * @param {Number} height The new height
7843          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7844          * @return {Roo.Element} this
7845          */
7846         setBounds : function(x, y, width, height, animate){
7847             if(!animate || !A){
7848                 this.setSize(width, height);
7849                 this.setLocation(x, y);
7850             }else{
7851                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7852                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7853                               this.preanim(arguments, 4), 'motion');
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * 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.
7860          * @param {Roo.lib.Region} region The region to fill
7861          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7862          * @return {Roo.Element} this
7863          */
7864         setRegion : function(region, animate){
7865             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7866             return this;
7867         },
7868
7869         /**
7870          * Appends an event handler
7871          *
7872          * @param {String}   eventName     The type of event to append
7873          * @param {Function} fn        The method the event invokes
7874          * @param {Object} scope       (optional) The scope (this object) of the fn
7875          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7876          */
7877         addListener : function(eventName, fn, scope, options){
7878             if (this.dom) {
7879                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7880             }
7881         },
7882
7883         /**
7884          * Removes an event handler from this element
7885          * @param {String} eventName the type of event to remove
7886          * @param {Function} fn the method the event invokes
7887          * @return {Roo.Element} this
7888          */
7889         removeListener : function(eventName, fn){
7890             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7891             return this;
7892         },
7893
7894         /**
7895          * Removes all previous added listeners from this element
7896          * @return {Roo.Element} this
7897          */
7898         removeAllListeners : function(){
7899             E.purgeElement(this.dom);
7900             return this;
7901         },
7902
7903         relayEvent : function(eventName, observable){
7904             this.on(eventName, function(e){
7905                 observable.fireEvent(eventName, e);
7906             });
7907         },
7908
7909         /**
7910          * Set the opacity of the element
7911          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7913          * @return {Roo.Element} this
7914          */
7915          setOpacity : function(opacity, animate){
7916             if(!animate || !A){
7917                 var s = this.dom.style;
7918                 if(Roo.isIE){
7919                     s.zoom = 1;
7920                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7921                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7922                 }else{
7923                     s.opacity = opacity;
7924                 }
7925             }else{
7926                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7927             }
7928             return this;
7929         },
7930
7931         /**
7932          * Gets the left X coordinate
7933          * @param {Boolean} local True to get the local css position instead of page coordinate
7934          * @return {Number}
7935          */
7936         getLeft : function(local){
7937             if(!local){
7938                 return this.getX();
7939             }else{
7940                 return parseInt(this.getStyle("left"), 10) || 0;
7941             }
7942         },
7943
7944         /**
7945          * Gets the right X coordinate of the element (element X position + element width)
7946          * @param {Boolean} local True to get the local css position instead of page coordinate
7947          * @return {Number}
7948          */
7949         getRight : function(local){
7950             if(!local){
7951                 return this.getX() + this.getWidth();
7952             }else{
7953                 return (this.getLeft(true) + this.getWidth()) || 0;
7954             }
7955         },
7956
7957         /**
7958          * Gets the top Y coordinate
7959          * @param {Boolean} local True to get the local css position instead of page coordinate
7960          * @return {Number}
7961          */
7962         getTop : function(local) {
7963             if(!local){
7964                 return this.getY();
7965             }else{
7966                 return parseInt(this.getStyle("top"), 10) || 0;
7967             }
7968         },
7969
7970         /**
7971          * Gets the bottom Y coordinate of the element (element Y position + element height)
7972          * @param {Boolean} local True to get the local css position instead of page coordinate
7973          * @return {Number}
7974          */
7975         getBottom : function(local){
7976             if(!local){
7977                 return this.getY() + this.getHeight();
7978             }else{
7979                 return (this.getTop(true) + this.getHeight()) || 0;
7980             }
7981         },
7982
7983         /**
7984         * Initializes positioning on this element. If a desired position is not passed, it will make the
7985         * the element positioned relative IF it is not already positioned.
7986         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7987         * @param {Number} zIndex (optional) The zIndex to apply
7988         * @param {Number} x (optional) Set the page X position
7989         * @param {Number} y (optional) Set the page Y position
7990         */
7991         position : function(pos, zIndex, x, y){
7992             if(!pos){
7993                if(this.getStyle('position') == 'static'){
7994                    this.setStyle('position', 'relative');
7995                }
7996             }else{
7997                 this.setStyle("position", pos);
7998             }
7999             if(zIndex){
8000                 this.setStyle("z-index", zIndex);
8001             }
8002             if(x !== undefined && y !== undefined){
8003                 this.setXY([x, y]);
8004             }else if(x !== undefined){
8005                 this.setX(x);
8006             }else if(y !== undefined){
8007                 this.setY(y);
8008             }
8009         },
8010
8011         /**
8012         * Clear positioning back to the default when the document was loaded
8013         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8014         * @return {Roo.Element} this
8015          */
8016         clearPositioning : function(value){
8017             value = value ||'';
8018             this.setStyle({
8019                 "left": value,
8020                 "right": value,
8021                 "top": value,
8022                 "bottom": value,
8023                 "z-index": "",
8024                 "position" : "static"
8025             });
8026             return this;
8027         },
8028
8029         /**
8030         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8031         * snapshot before performing an update and then restoring the element.
8032         * @return {Object}
8033         */
8034         getPositioning : function(){
8035             var l = this.getStyle("left");
8036             var t = this.getStyle("top");
8037             return {
8038                 "position" : this.getStyle("position"),
8039                 "left" : l,
8040                 "right" : l ? "" : this.getStyle("right"),
8041                 "top" : t,
8042                 "bottom" : t ? "" : this.getStyle("bottom"),
8043                 "z-index" : this.getStyle("z-index")
8044             };
8045         },
8046
8047         /**
8048          * Gets the width of the border(s) for the specified side(s)
8049          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8050          * passing lr would get the border (l)eft width + the border (r)ight width.
8051          * @return {Number} The width of the sides passed added together
8052          */
8053         getBorderWidth : function(side){
8054             return this.addStyles(side, El.borders);
8055         },
8056
8057         /**
8058          * Gets the width of the padding(s) for the specified side(s)
8059          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8060          * passing lr would get the padding (l)eft + the padding (r)ight.
8061          * @return {Number} The padding of the sides passed added together
8062          */
8063         getPadding : function(side){
8064             return this.addStyles(side, El.paddings);
8065         },
8066
8067         /**
8068         * Set positioning with an object returned by getPositioning().
8069         * @param {Object} posCfg
8070         * @return {Roo.Element} this
8071          */
8072         setPositioning : function(pc){
8073             this.applyStyles(pc);
8074             if(pc.right == "auto"){
8075                 this.dom.style.right = "";
8076             }
8077             if(pc.bottom == "auto"){
8078                 this.dom.style.bottom = "";
8079             }
8080             return this;
8081         },
8082
8083         // private
8084         fixDisplay : function(){
8085             if(this.getStyle("display") == "none"){
8086                 this.setStyle("visibility", "hidden");
8087                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8088                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8089                     this.setStyle("display", "block");
8090                 }
8091             }
8092         },
8093
8094         /**
8095          * Quick set left and top adding default units
8096          * @param {String} left The left CSS property value
8097          * @param {String} top The top CSS property value
8098          * @return {Roo.Element} this
8099          */
8100          setLeftTop : function(left, top){
8101             this.dom.style.left = this.addUnits(left);
8102             this.dom.style.top = this.addUnits(top);
8103             return this;
8104         },
8105
8106         /**
8107          * Move this element relative to its current position.
8108          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8109          * @param {Number} distance How far to move the element in pixels
8110          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8111          * @return {Roo.Element} this
8112          */
8113          move : function(direction, distance, animate){
8114             var xy = this.getXY();
8115             direction = direction.toLowerCase();
8116             switch(direction){
8117                 case "l":
8118                 case "left":
8119                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8120                     break;
8121                case "r":
8122                case "right":
8123                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8124                     break;
8125                case "t":
8126                case "top":
8127                case "up":
8128                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8129                     break;
8130                case "b":
8131                case "bottom":
8132                case "down":
8133                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8134                     break;
8135             }
8136             return this;
8137         },
8138
8139         /**
8140          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8141          * @return {Roo.Element} this
8142          */
8143         clip : function(){
8144             if(!this.isClipped){
8145                this.isClipped = true;
8146                this.originalClip = {
8147                    "o": this.getStyle("overflow"),
8148                    "x": this.getStyle("overflow-x"),
8149                    "y": this.getStyle("overflow-y")
8150                };
8151                this.setStyle("overflow", "hidden");
8152                this.setStyle("overflow-x", "hidden");
8153                this.setStyle("overflow-y", "hidden");
8154             }
8155             return this;
8156         },
8157
8158         /**
8159          *  Return clipping (overflow) to original clipping before clip() was called
8160          * @return {Roo.Element} this
8161          */
8162         unclip : function(){
8163             if(this.isClipped){
8164                 this.isClipped = false;
8165                 var o = this.originalClip;
8166                 if(o.o){this.setStyle("overflow", o.o);}
8167                 if(o.x){this.setStyle("overflow-x", o.x);}
8168                 if(o.y){this.setStyle("overflow-y", o.y);}
8169             }
8170             return this;
8171         },
8172
8173
8174         /**
8175          * Gets the x,y coordinates specified by the anchor position on the element.
8176          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8177          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8178          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8179          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8180          * @return {Array} [x, y] An array containing the element's x and y coordinates
8181          */
8182         getAnchorXY : function(anchor, local, s){
8183             //Passing a different size is useful for pre-calculating anchors,
8184             //especially for anchored animations that change the el size.
8185
8186             var w, h, vp = false;
8187             if(!s){
8188                 var d = this.dom;
8189                 if(d == document.body || d == document){
8190                     vp = true;
8191                     w = D.getViewWidth(); h = D.getViewHeight();
8192                 }else{
8193                     w = this.getWidth(); h = this.getHeight();
8194                 }
8195             }else{
8196                 w = s.width;  h = s.height;
8197             }
8198             var x = 0, y = 0, r = Math.round;
8199             switch((anchor || "tl").toLowerCase()){
8200                 case "c":
8201                     x = r(w*.5);
8202                     y = r(h*.5);
8203                 break;
8204                 case "t":
8205                     x = r(w*.5);
8206                     y = 0;
8207                 break;
8208                 case "l":
8209                     x = 0;
8210                     y = r(h*.5);
8211                 break;
8212                 case "r":
8213                     x = w;
8214                     y = r(h*.5);
8215                 break;
8216                 case "b":
8217                     x = r(w*.5);
8218                     y = h;
8219                 break;
8220                 case "tl":
8221                     x = 0;
8222                     y = 0;
8223                 break;
8224                 case "bl":
8225                     x = 0;
8226                     y = h;
8227                 break;
8228                 case "br":
8229                     x = w;
8230                     y = h;
8231                 break;
8232                 case "tr":
8233                     x = w;
8234                     y = 0;
8235                 break;
8236             }
8237             if(local === true){
8238                 return [x, y];
8239             }
8240             if(vp){
8241                 var sc = this.getScroll();
8242                 return [x + sc.left, y + sc.top];
8243             }
8244             //Add the element's offset xy
8245             var o = this.getXY();
8246             return [x+o[0], y+o[1]];
8247         },
8248
8249         /**
8250          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8251          * supported position values.
8252          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8253          * @param {String} position The position to align to.
8254          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8255          * @return {Array} [x, y]
8256          */
8257         getAlignToXY : function(el, p, o){
8258             el = Roo.get(el);
8259             var d = this.dom;
8260             if(!el.dom){
8261                 throw "Element.alignTo with an element that doesn't exist";
8262             }
8263             var c = false; //constrain to viewport
8264             var p1 = "", p2 = "";
8265             o = o || [0,0];
8266
8267             if(!p){
8268                 p = "tl-bl";
8269             }else if(p == "?"){
8270                 p = "tl-bl?";
8271             }else if(p.indexOf("-") == -1){
8272                 p = "tl-" + p;
8273             }
8274             p = p.toLowerCase();
8275             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8276             if(!m){
8277                throw "Element.alignTo with an invalid alignment " + p;
8278             }
8279             p1 = m[1]; p2 = m[2]; c = !!m[3];
8280
8281             //Subtract the aligned el's internal xy from the target's offset xy
8282             //plus custom offset to get the aligned el's new offset xy
8283             var a1 = this.getAnchorXY(p1, true);
8284             var a2 = el.getAnchorXY(p2, false);
8285             var x = a2[0] - a1[0] + o[0];
8286             var y = a2[1] - a1[1] + o[1];
8287             if(c){
8288                 //constrain the aligned el to viewport if necessary
8289                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8290                 // 5px of margin for ie
8291                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8292
8293                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8294                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8295                 //otherwise swap the aligned el to the opposite border of the target.
8296                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8297                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8298                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8299                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8300
8301                var doc = document;
8302                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8303                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8304
8305                if((x+w) > dw + scrollX){
8306                     x = swapX ? r.left-w : dw+scrollX-w;
8307                 }
8308                if(x < scrollX){
8309                    x = swapX ? r.right : scrollX;
8310                }
8311                if((y+h) > dh + scrollY){
8312                     y = swapY ? r.top-h : dh+scrollY-h;
8313                 }
8314                if (y < scrollY){
8315                    y = swapY ? r.bottom : scrollY;
8316                }
8317             }
8318             return [x,y];
8319         },
8320
8321         // private
8322         getConstrainToXY : function(){
8323             var os = {top:0, left:0, bottom:0, right: 0};
8324
8325             return function(el, local, offsets, proposedXY){
8326                 el = Roo.get(el);
8327                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8328
8329                 var vw, vh, vx = 0, vy = 0;
8330                 if(el.dom == document.body || el.dom == document){
8331                     vw = Roo.lib.Dom.getViewWidth();
8332                     vh = Roo.lib.Dom.getViewHeight();
8333                 }else{
8334                     vw = el.dom.clientWidth;
8335                     vh = el.dom.clientHeight;
8336                     if(!local){
8337                         var vxy = el.getXY();
8338                         vx = vxy[0];
8339                         vy = vxy[1];
8340                     }
8341                 }
8342
8343                 var s = el.getScroll();
8344
8345                 vx += offsets.left + s.left;
8346                 vy += offsets.top + s.top;
8347
8348                 vw -= offsets.right;
8349                 vh -= offsets.bottom;
8350
8351                 var vr = vx+vw;
8352                 var vb = vy+vh;
8353
8354                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8355                 var x = xy[0], y = xy[1];
8356                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8357
8358                 // only move it if it needs it
8359                 var moved = false;
8360
8361                 // first validate right/bottom
8362                 if((x + w) > vr){
8363                     x = vr - w;
8364                     moved = true;
8365                 }
8366                 if((y + h) > vb){
8367                     y = vb - h;
8368                     moved = true;
8369                 }
8370                 // then make sure top/left isn't negative
8371                 if(x < vx){
8372                     x = vx;
8373                     moved = true;
8374                 }
8375                 if(y < vy){
8376                     y = vy;
8377                     moved = true;
8378                 }
8379                 return moved ? [x, y] : false;
8380             };
8381         }(),
8382
8383         // private
8384         adjustForConstraints : function(xy, parent, offsets){
8385             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8386         },
8387
8388         /**
8389          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8390          * document it aligns it to the viewport.
8391          * The position parameter is optional, and can be specified in any one of the following formats:
8392          * <ul>
8393          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8394          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8395          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8396          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8397          *   <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
8398          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8399          * </ul>
8400          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8401          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8402          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8403          * that specified in order to enforce the viewport constraints.
8404          * Following are all of the supported anchor positions:
8405     <pre>
8406     Value  Description
8407     -----  -----------------------------
8408     tl     The top left corner (default)
8409     t      The center of the top edge
8410     tr     The top right corner
8411     l      The center of the left edge
8412     c      In the center of the element
8413     r      The center of the right edge
8414     bl     The bottom left corner
8415     b      The center of the bottom edge
8416     br     The bottom right corner
8417     </pre>
8418     Example Usage:
8419     <pre><code>
8420     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8421     el.alignTo("other-el");
8422
8423     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8424     el.alignTo("other-el", "tr?");
8425
8426     // align the bottom right corner of el with the center left edge of other-el
8427     el.alignTo("other-el", "br-l?");
8428
8429     // align the center of el with the bottom left corner of other-el and
8430     // adjust the x position by -6 pixels (and the y position by 0)
8431     el.alignTo("other-el", "c-bl", [-6, 0]);
8432     </code></pre>
8433          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8434          * @param {String} position The position to align to.
8435          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8436          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8437          * @return {Roo.Element} this
8438          */
8439         alignTo : function(element, position, offsets, animate){
8440             var xy = this.getAlignToXY(element, position, offsets);
8441             this.setXY(xy, this.preanim(arguments, 3));
8442             return this;
8443         },
8444
8445         /**
8446          * Anchors an element to another element and realigns it when the window is resized.
8447          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8448          * @param {String} position The position to align to.
8449          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8450          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8451          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8452          * is a number, it is used as the buffer delay (defaults to 50ms).
8453          * @param {Function} callback The function to call after the animation finishes
8454          * @return {Roo.Element} this
8455          */
8456         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8457             var action = function(){
8458                 this.alignTo(el, alignment, offsets, animate);
8459                 Roo.callback(callback, this);
8460             };
8461             Roo.EventManager.onWindowResize(action, this);
8462             var tm = typeof monitorScroll;
8463             if(tm != 'undefined'){
8464                 Roo.EventManager.on(window, 'scroll', action, this,
8465                     {buffer: tm == 'number' ? monitorScroll : 50});
8466             }
8467             action.call(this); // align immediately
8468             return this;
8469         },
8470         /**
8471          * Clears any opacity settings from this element. Required in some cases for IE.
8472          * @return {Roo.Element} this
8473          */
8474         clearOpacity : function(){
8475             if (window.ActiveXObject) {
8476                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8477                     this.dom.style.filter = "";
8478                 }
8479             } else {
8480                 this.dom.style.opacity = "";
8481                 this.dom.style["-moz-opacity"] = "";
8482                 this.dom.style["-khtml-opacity"] = "";
8483             }
8484             return this;
8485         },
8486
8487         /**
8488          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8489          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8490          * @return {Roo.Element} this
8491          */
8492         hide : function(animate){
8493             this.setVisible(false, this.preanim(arguments, 0));
8494             return this;
8495         },
8496
8497         /**
8498         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8499         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8500          * @return {Roo.Element} this
8501          */
8502         show : function(animate){
8503             this.setVisible(true, this.preanim(arguments, 0));
8504             return this;
8505         },
8506
8507         /**
8508          * @private Test if size has a unit, otherwise appends the default
8509          */
8510         addUnits : function(size){
8511             return Roo.Element.addUnits(size, this.defaultUnit);
8512         },
8513
8514         /**
8515          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8516          * @return {Roo.Element} this
8517          */
8518         beginMeasure : function(){
8519             var el = this.dom;
8520             if(el.offsetWidth || el.offsetHeight){
8521                 return this; // offsets work already
8522             }
8523             var changed = [];
8524             var p = this.dom, b = document.body; // start with this element
8525             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8526                 var pe = Roo.get(p);
8527                 if(pe.getStyle('display') == 'none'){
8528                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8529                     p.style.visibility = "hidden";
8530                     p.style.display = "block";
8531                 }
8532                 p = p.parentNode;
8533             }
8534             this._measureChanged = changed;
8535             return this;
8536
8537         },
8538
8539         /**
8540          * Restores displays to before beginMeasure was called
8541          * @return {Roo.Element} this
8542          */
8543         endMeasure : function(){
8544             var changed = this._measureChanged;
8545             if(changed){
8546                 for(var i = 0, len = changed.length; i < len; i++) {
8547                     var r = changed[i];
8548                     r.el.style.visibility = r.visibility;
8549                     r.el.style.display = "none";
8550                 }
8551                 this._measureChanged = null;
8552             }
8553             return this;
8554         },
8555
8556         /**
8557         * Update the innerHTML of this element, optionally searching for and processing scripts
8558         * @param {String} html The new HTML
8559         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8560         * @param {Function} callback For async script loading you can be noticed when the update completes
8561         * @return {Roo.Element} this
8562          */
8563         update : function(html, loadScripts, callback){
8564             if(typeof html == "undefined"){
8565                 html = "";
8566             }
8567             if(loadScripts !== true){
8568                 this.dom.innerHTML = html;
8569                 if(typeof callback == "function"){
8570                     callback();
8571                 }
8572                 return this;
8573             }
8574             var id = Roo.id();
8575             var dom = this.dom;
8576
8577             html += '<span id="' + id + '"></span>';
8578
8579             E.onAvailable(id, function(){
8580                 var hd = document.getElementsByTagName("head")[0];
8581                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8582                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8583                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8584
8585                 var match;
8586                 while(match = re.exec(html)){
8587                     var attrs = match[1];
8588                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8589                     if(srcMatch && srcMatch[2]){
8590                        var s = document.createElement("script");
8591                        s.src = srcMatch[2];
8592                        var typeMatch = attrs.match(typeRe);
8593                        if(typeMatch && typeMatch[2]){
8594                            s.type = typeMatch[2];
8595                        }
8596                        hd.appendChild(s);
8597                     }else if(match[2] && match[2].length > 0){
8598                         if(window.execScript) {
8599                            window.execScript(match[2]);
8600                         } else {
8601                             /**
8602                              * eval:var:id
8603                              * eval:var:dom
8604                              * eval:var:html
8605                              * 
8606                              */
8607                            window.eval(match[2]);
8608                         }
8609                     }
8610                 }
8611                 var el = document.getElementById(id);
8612                 if(el){el.parentNode.removeChild(el);}
8613                 if(typeof callback == "function"){
8614                     callback();
8615                 }
8616             });
8617             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8618             return this;
8619         },
8620
8621         /**
8622          * Direct access to the UpdateManager update() method (takes the same parameters).
8623          * @param {String/Function} url The url for this request or a function to call to get the url
8624          * @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}
8625          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8626          * @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.
8627          * @return {Roo.Element} this
8628          */
8629         load : function(){
8630             var um = this.getUpdateManager();
8631             um.update.apply(um, arguments);
8632             return this;
8633         },
8634
8635         /**
8636         * Gets this element's UpdateManager
8637         * @return {Roo.UpdateManager} The UpdateManager
8638         */
8639         getUpdateManager : function(){
8640             if(!this.updateManager){
8641                 this.updateManager = new Roo.UpdateManager(this);
8642             }
8643             return this.updateManager;
8644         },
8645
8646         /**
8647          * Disables text selection for this element (normalized across browsers)
8648          * @return {Roo.Element} this
8649          */
8650         unselectable : function(){
8651             this.dom.unselectable = "on";
8652             this.swallowEvent("selectstart", true);
8653             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8654             this.addClass("x-unselectable");
8655             return this;
8656         },
8657
8658         /**
8659         * Calculates the x, y to center this element on the screen
8660         * @return {Array} The x, y values [x, y]
8661         */
8662         getCenterXY : function(){
8663             return this.getAlignToXY(document, 'c-c');
8664         },
8665
8666         /**
8667         * Centers the Element in either the viewport, or another Element.
8668         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8669         */
8670         center : function(centerIn){
8671             this.alignTo(centerIn || document, 'c-c');
8672             return this;
8673         },
8674
8675         /**
8676          * Tests various css rules/browsers to determine if this element uses a border box
8677          * @return {Boolean}
8678          */
8679         isBorderBox : function(){
8680             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8681         },
8682
8683         /**
8684          * Return a box {x, y, width, height} that can be used to set another elements
8685          * size/location to match this element.
8686          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8687          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8688          * @return {Object} box An object in the format {x, y, width, height}
8689          */
8690         getBox : function(contentBox, local){
8691             var xy;
8692             if(!local){
8693                 xy = this.getXY();
8694             }else{
8695                 var left = parseInt(this.getStyle("left"), 10) || 0;
8696                 var top = parseInt(this.getStyle("top"), 10) || 0;
8697                 xy = [left, top];
8698             }
8699             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8700             if(!contentBox){
8701                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8702             }else{
8703                 var l = this.getBorderWidth("l")+this.getPadding("l");
8704                 var r = this.getBorderWidth("r")+this.getPadding("r");
8705                 var t = this.getBorderWidth("t")+this.getPadding("t");
8706                 var b = this.getBorderWidth("b")+this.getPadding("b");
8707                 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)};
8708             }
8709             bx.right = bx.x + bx.width;
8710             bx.bottom = bx.y + bx.height;
8711             return bx;
8712         },
8713
8714         /**
8715          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8716          for more information about the sides.
8717          * @param {String} sides
8718          * @return {Number}
8719          */
8720         getFrameWidth : function(sides, onlyContentBox){
8721             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8722         },
8723
8724         /**
8725          * 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.
8726          * @param {Object} box The box to fill {x, y, width, height}
8727          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8729          * @return {Roo.Element} this
8730          */
8731         setBox : function(box, adjust, animate){
8732             var w = box.width, h = box.height;
8733             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8734                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8735                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8736             }
8737             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8738             return this;
8739         },
8740
8741         /**
8742          * Forces the browser to repaint this element
8743          * @return {Roo.Element} this
8744          */
8745          repaint : function(){
8746             var dom = this.dom;
8747             this.addClass("x-repaint");
8748             setTimeout(function(){
8749                 Roo.get(dom).removeClass("x-repaint");
8750             }, 1);
8751             return this;
8752         },
8753
8754         /**
8755          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8756          * then it returns the calculated width of the sides (see getPadding)
8757          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8758          * @return {Object/Number}
8759          */
8760         getMargins : function(side){
8761             if(!side){
8762                 return {
8763                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8764                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8765                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8766                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8767                 };
8768             }else{
8769                 return this.addStyles(side, El.margins);
8770              }
8771         },
8772
8773         // private
8774         addStyles : function(sides, styles){
8775             var val = 0, v, w;
8776             for(var i = 0, len = sides.length; i < len; i++){
8777                 v = this.getStyle(styles[sides.charAt(i)]);
8778                 if(v){
8779                      w = parseInt(v, 10);
8780                      if(w){ val += w; }
8781                 }
8782             }
8783             return val;
8784         },
8785
8786         /**
8787          * Creates a proxy element of this element
8788          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8789          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8790          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8791          * @return {Roo.Element} The new proxy element
8792          */
8793         createProxy : function(config, renderTo, matchBox){
8794             if(renderTo){
8795                 renderTo = Roo.getDom(renderTo);
8796             }else{
8797                 renderTo = document.body;
8798             }
8799             config = typeof config == "object" ?
8800                 config : {tag : "div", cls: config};
8801             var proxy = Roo.DomHelper.append(renderTo, config, true);
8802             if(matchBox){
8803                proxy.setBox(this.getBox());
8804             }
8805             return proxy;
8806         },
8807
8808         /**
8809          * Puts a mask over this element to disable user interaction. Requires core.css.
8810          * This method can only be applied to elements which accept child nodes.
8811          * @param {String} msg (optional) A message to display in the mask
8812          * @param {String} msgCls (optional) A css class to apply to the msg element
8813          * @return {Element} The mask  element
8814          */
8815         mask : function(msg, msgCls){
8816             if(this.getStyle("position") == "static"){
8817                 this.setStyle("position", "relative");
8818             }
8819             if(!this._mask){
8820                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8821             }
8822             this.addClass("x-masked");
8823             this._mask.setDisplayed(true);
8824             if(typeof msg == 'string'){
8825                 if(!this._maskMsg){
8826                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8827                 }
8828                 var mm = this._maskMsg;
8829                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8830                 mm.dom.firstChild.innerHTML = msg;
8831                 mm.setDisplayed(true);
8832                 mm.center(this);
8833             }
8834             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8835                 this._mask.setHeight(this.getHeight());
8836             }
8837             return this._mask;
8838         },
8839
8840         /**
8841          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8842          * it is cached for reuse.
8843          */
8844         unmask : function(removeEl){
8845             if(this._mask){
8846                 if(removeEl === true){
8847                     this._mask.remove();
8848                     delete this._mask;
8849                     if(this._maskMsg){
8850                         this._maskMsg.remove();
8851                         delete this._maskMsg;
8852                     }
8853                 }else{
8854                     this._mask.setDisplayed(false);
8855                     if(this._maskMsg){
8856                         this._maskMsg.setDisplayed(false);
8857                     }
8858                 }
8859             }
8860             this.removeClass("x-masked");
8861         },
8862
8863         /**
8864          * Returns true if this element is masked
8865          * @return {Boolean}
8866          */
8867         isMasked : function(){
8868             return this._mask && this._mask.isVisible();
8869         },
8870
8871         /**
8872          * Creates an iframe shim for this element to keep selects and other windowed objects from
8873          * showing through.
8874          * @return {Roo.Element} The new shim element
8875          */
8876         createShim : function(){
8877             var el = document.createElement('iframe');
8878             el.frameBorder = 'no';
8879             el.className = 'roo-shim';
8880             if(Roo.isIE && Roo.isSecure){
8881                 el.src = Roo.SSL_SECURE_URL;
8882             }
8883             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8884             shim.autoBoxAdjust = false;
8885             return shim;
8886         },
8887
8888         /**
8889          * Removes this element from the DOM and deletes it from the cache
8890          */
8891         remove : function(){
8892             if(this.dom.parentNode){
8893                 this.dom.parentNode.removeChild(this.dom);
8894             }
8895             delete El.cache[this.dom.id];
8896         },
8897
8898         /**
8899          * Sets up event handlers to add and remove a css class when the mouse is over this element
8900          * @param {String} className
8901          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8902          * mouseout events for children elements
8903          * @return {Roo.Element} this
8904          */
8905         addClassOnOver : function(className, preventFlicker){
8906             this.on("mouseover", function(){
8907                 Roo.fly(this, '_internal').addClass(className);
8908             }, this.dom);
8909             var removeFn = function(e){
8910                 if(preventFlicker !== true || !e.within(this, true)){
8911                     Roo.fly(this, '_internal').removeClass(className);
8912                 }
8913             };
8914             this.on("mouseout", removeFn, this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Sets up event handlers to add and remove a css class when this element has the focus
8920          * @param {String} className
8921          * @return {Roo.Element} this
8922          */
8923         addClassOnFocus : function(className){
8924             this.on("focus", function(){
8925                 Roo.fly(this, '_internal').addClass(className);
8926             }, this.dom);
8927             this.on("blur", function(){
8928                 Roo.fly(this, '_internal').removeClass(className);
8929             }, this.dom);
8930             return this;
8931         },
8932         /**
8933          * 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)
8934          * @param {String} className
8935          * @return {Roo.Element} this
8936          */
8937         addClassOnClick : function(className){
8938             var dom = this.dom;
8939             this.on("mousedown", function(){
8940                 Roo.fly(dom, '_internal').addClass(className);
8941                 var d = Roo.get(document);
8942                 var fn = function(){
8943                     Roo.fly(dom, '_internal').removeClass(className);
8944                     d.removeListener("mouseup", fn);
8945                 };
8946                 d.on("mouseup", fn);
8947             });
8948             return this;
8949         },
8950
8951         /**
8952          * Stops the specified event from bubbling and optionally prevents the default action
8953          * @param {String} eventName
8954          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8955          * @return {Roo.Element} this
8956          */
8957         swallowEvent : function(eventName, preventDefault){
8958             var fn = function(e){
8959                 e.stopPropagation();
8960                 if(preventDefault){
8961                     e.preventDefault();
8962                 }
8963             };
8964             if(eventName instanceof Array){
8965                 for(var i = 0, len = eventName.length; i < len; i++){
8966                      this.on(eventName[i], fn);
8967                 }
8968                 return this;
8969             }
8970             this.on(eventName, fn);
8971             return this;
8972         },
8973
8974         /**
8975          * @private
8976          */
8977       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8978
8979         /**
8980          * Sizes this element to its parent element's dimensions performing
8981          * neccessary box adjustments.
8982          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8983          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8984          * @return {Roo.Element} this
8985          */
8986         fitToParent : function(monitorResize, targetParent) {
8987           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8988           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8989           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8990             return;
8991           }
8992           var p = Roo.get(targetParent || this.dom.parentNode);
8993           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8994           if (monitorResize === true) {
8995             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8996             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8997           }
8998           return this;
8999         },
9000
9001         /**
9002          * Gets the next sibling, skipping text nodes
9003          * @return {HTMLElement} The next sibling or null
9004          */
9005         getNextSibling : function(){
9006             var n = this.dom.nextSibling;
9007             while(n && n.nodeType != 1){
9008                 n = n.nextSibling;
9009             }
9010             return n;
9011         },
9012
9013         /**
9014          * Gets the previous sibling, skipping text nodes
9015          * @return {HTMLElement} The previous sibling or null
9016          */
9017         getPrevSibling : function(){
9018             var n = this.dom.previousSibling;
9019             while(n && n.nodeType != 1){
9020                 n = n.previousSibling;
9021             }
9022             return n;
9023         },
9024
9025
9026         /**
9027          * Appends the passed element(s) to this element
9028          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9029          * @return {Roo.Element} this
9030          */
9031         appendChild: function(el){
9032             el = Roo.get(el);
9033             el.appendTo(this);
9034             return this;
9035         },
9036
9037         /**
9038          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9039          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9040          * automatically generated with the specified attributes.
9041          * @param {HTMLElement} insertBefore (optional) a child element of this element
9042          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9043          * @return {Roo.Element} The new child element
9044          */
9045         createChild: function(config, insertBefore, returnDom){
9046             config = config || {tag:'div'};
9047             if(insertBefore){
9048                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9049             }
9050             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9051         },
9052
9053         /**
9054          * Appends this element to the passed element
9055          * @param {String/HTMLElement/Element} el The new parent element
9056          * @return {Roo.Element} this
9057          */
9058         appendTo: function(el){
9059             el = Roo.getDom(el);
9060             el.appendChild(this.dom);
9061             return this;
9062         },
9063
9064         /**
9065          * Inserts this element before the passed element in the DOM
9066          * @param {String/HTMLElement/Element} el The element to insert before
9067          * @return {Roo.Element} this
9068          */
9069         insertBefore: function(el){
9070             el = Roo.getDom(el);
9071             el.parentNode.insertBefore(this.dom, el);
9072             return this;
9073         },
9074
9075         /**
9076          * Inserts this element after the passed element in the DOM
9077          * @param {String/HTMLElement/Element} el The element to insert after
9078          * @return {Roo.Element} this
9079          */
9080         insertAfter: function(el){
9081             el = Roo.getDom(el);
9082             el.parentNode.insertBefore(this.dom, el.nextSibling);
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9088          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9089          * @return {Roo.Element} The new child
9090          */
9091         insertFirst: function(el, returnDom){
9092             el = el || {};
9093             if(typeof el == 'object' && !el.nodeType){ // dh config
9094                 return this.createChild(el, this.dom.firstChild, returnDom);
9095             }else{
9096                 el = Roo.getDom(el);
9097                 this.dom.insertBefore(el, this.dom.firstChild);
9098                 return !returnDom ? Roo.get(el) : el;
9099             }
9100         },
9101
9102         /**
9103          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9104          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9105          * @param {String} where (optional) 'before' or 'after' defaults to before
9106          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9107          * @return {Roo.Element} the inserted Element
9108          */
9109         insertSibling: function(el, where, returnDom){
9110             where = where ? where.toLowerCase() : 'before';
9111             el = el || {};
9112             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9113
9114             if(typeof el == 'object' && !el.nodeType){ // dh config
9115                 if(where == 'after' && !this.dom.nextSibling){
9116                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9117                 }else{
9118                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9119                 }
9120
9121             }else{
9122                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9123                             where == 'before' ? this.dom : this.dom.nextSibling);
9124                 if(!returnDom){
9125                     rt = Roo.get(rt);
9126                 }
9127             }
9128             return rt;
9129         },
9130
9131         /**
9132          * Creates and wraps this element with another element
9133          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9134          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9135          * @return {HTMLElement/Element} The newly created wrapper element
9136          */
9137         wrap: function(config, returnDom){
9138             if(!config){
9139                 config = {tag: "div"};
9140             }
9141             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9142             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9143             return newEl;
9144         },
9145
9146         /**
9147          * Replaces the passed element with this element
9148          * @param {String/HTMLElement/Element} el The element to replace
9149          * @return {Roo.Element} this
9150          */
9151         replace: function(el){
9152             el = Roo.get(el);
9153             this.insertBefore(el);
9154             el.remove();
9155             return this;
9156         },
9157
9158         /**
9159          * Inserts an html fragment into this element
9160          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9161          * @param {String} html The HTML fragment
9162          * @param {Boolean} returnEl True to return an Roo.Element
9163          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9164          */
9165         insertHtml : function(where, html, returnEl){
9166             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9167             return returnEl ? Roo.get(el) : el;
9168         },
9169
9170         /**
9171          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9172          * @param {Object} o The object with the attributes
9173          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9174          * @return {Roo.Element} this
9175          */
9176         set : function(o, useSet){
9177             var el = this.dom;
9178             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9179             for(var attr in o){
9180                 if(attr == "style" || typeof o[attr] == "function") continue;
9181                 if(attr=="cls"){
9182                     el.className = o["cls"];
9183                 }else{
9184                     if(useSet) el.setAttribute(attr, o[attr]);
9185                     else el[attr] = o[attr];
9186                 }
9187             }
9188             if(o.style){
9189                 Roo.DomHelper.applyStyles(el, o.style);
9190             }
9191             return this;
9192         },
9193
9194         /**
9195          * Convenience method for constructing a KeyMap
9196          * @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:
9197          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9198          * @param {Function} fn The function to call
9199          * @param {Object} scope (optional) The scope of the function
9200          * @return {Roo.KeyMap} The KeyMap created
9201          */
9202         addKeyListener : function(key, fn, scope){
9203             var config;
9204             if(typeof key != "object" || key instanceof Array){
9205                 config = {
9206                     key: key,
9207                     fn: fn,
9208                     scope: scope
9209                 };
9210             }else{
9211                 config = {
9212                     key : key.key,
9213                     shift : key.shift,
9214                     ctrl : key.ctrl,
9215                     alt : key.alt,
9216                     fn: fn,
9217                     scope: scope
9218                 };
9219             }
9220             return new Roo.KeyMap(this, config);
9221         },
9222
9223         /**
9224          * Creates a KeyMap for this element
9225          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9226          * @return {Roo.KeyMap} The KeyMap created
9227          */
9228         addKeyMap : function(config){
9229             return new Roo.KeyMap(this, config);
9230         },
9231
9232         /**
9233          * Returns true if this element is scrollable.
9234          * @return {Boolean}
9235          */
9236          isScrollable : function(){
9237             var dom = this.dom;
9238             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9239         },
9240
9241         /**
9242          * 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().
9243          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9244          * @param {Number} value The new scroll value
9245          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9246          * @return {Element} this
9247          */
9248
9249         scrollTo : function(side, value, animate){
9250             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9251             if(!animate || !A){
9252                 this.dom[prop] = value;
9253             }else{
9254                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9255                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9256             }
9257             return this;
9258         },
9259
9260         /**
9261          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9262          * within this element's scrollable range.
9263          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9264          * @param {Number} distance How far to scroll the element in pixels
9265          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9266          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9267          * was scrolled as far as it could go.
9268          */
9269          scroll : function(direction, distance, animate){
9270              if(!this.isScrollable()){
9271                  return;
9272              }
9273              var el = this.dom;
9274              var l = el.scrollLeft, t = el.scrollTop;
9275              var w = el.scrollWidth, h = el.scrollHeight;
9276              var cw = el.clientWidth, ch = el.clientHeight;
9277              direction = direction.toLowerCase();
9278              var scrolled = false;
9279              var a = this.preanim(arguments, 2);
9280              switch(direction){
9281                  case "l":
9282                  case "left":
9283                      if(w - l > cw){
9284                          var v = Math.min(l + distance, w-cw);
9285                          this.scrollTo("left", v, a);
9286                          scrolled = true;
9287                      }
9288                      break;
9289                 case "r":
9290                 case "right":
9291                      if(l > 0){
9292                          var v = Math.max(l - distance, 0);
9293                          this.scrollTo("left", v, a);
9294                          scrolled = true;
9295                      }
9296                      break;
9297                 case "t":
9298                 case "top":
9299                 case "up":
9300                      if(t > 0){
9301                          var v = Math.max(t - distance, 0);
9302                          this.scrollTo("top", v, a);
9303                          scrolled = true;
9304                      }
9305                      break;
9306                 case "b":
9307                 case "bottom":
9308                 case "down":
9309                      if(h - t > ch){
9310                          var v = Math.min(t + distance, h-ch);
9311                          this.scrollTo("top", v, a);
9312                          scrolled = true;
9313                      }
9314                      break;
9315              }
9316              return scrolled;
9317         },
9318
9319         /**
9320          * Translates the passed page coordinates into left/top css values for this element
9321          * @param {Number/Array} x The page x or an array containing [x, y]
9322          * @param {Number} y The page y
9323          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9324          */
9325         translatePoints : function(x, y){
9326             if(typeof x == 'object' || x instanceof Array){
9327                 y = x[1]; x = x[0];
9328             }
9329             var p = this.getStyle('position');
9330             var o = this.getXY();
9331
9332             var l = parseInt(this.getStyle('left'), 10);
9333             var t = parseInt(this.getStyle('top'), 10);
9334
9335             if(isNaN(l)){
9336                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9337             }
9338             if(isNaN(t)){
9339                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9340             }
9341
9342             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9343         },
9344
9345         /**
9346          * Returns the current scroll position of the element.
9347          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9348          */
9349         getScroll : function(){
9350             var d = this.dom, doc = document;
9351             if(d == doc || d == doc.body){
9352                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9353                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9354                 return {left: l, top: t};
9355             }else{
9356                 return {left: d.scrollLeft, top: d.scrollTop};
9357             }
9358         },
9359
9360         /**
9361          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9362          * are convert to standard 6 digit hex color.
9363          * @param {String} attr The css attribute
9364          * @param {String} defaultValue The default value to use when a valid color isn't found
9365          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9366          * YUI color anims.
9367          */
9368         getColor : function(attr, defaultValue, prefix){
9369             var v = this.getStyle(attr);
9370             if(!v || v == "transparent" || v == "inherit") {
9371                 return defaultValue;
9372             }
9373             var color = typeof prefix == "undefined" ? "#" : prefix;
9374             if(v.substr(0, 4) == "rgb("){
9375                 var rvs = v.slice(4, v.length -1).split(",");
9376                 for(var i = 0; i < 3; i++){
9377                     var h = parseInt(rvs[i]).toString(16);
9378                     if(h < 16){
9379                         h = "0" + h;
9380                     }
9381                     color += h;
9382                 }
9383             } else {
9384                 if(v.substr(0, 1) == "#"){
9385                     if(v.length == 4) {
9386                         for(var i = 1; i < 4; i++){
9387                             var c = v.charAt(i);
9388                             color +=  c + c;
9389                         }
9390                     }else if(v.length == 7){
9391                         color += v.substr(1);
9392                     }
9393                 }
9394             }
9395             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9396         },
9397
9398         /**
9399          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9400          * gradient background, rounded corners and a 4-way shadow.
9401          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9402          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9403          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9404          * @return {Roo.Element} this
9405          */
9406         boxWrap : function(cls){
9407             cls = cls || 'x-box';
9408             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9409             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9410             return el;
9411         },
9412
9413         /**
9414          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9415          * @param {String} namespace The namespace in which to look for the attribute
9416          * @param {String} name The attribute name
9417          * @return {String} The attribute value
9418          */
9419         getAttributeNS : Roo.isIE ? function(ns, name){
9420             var d = this.dom;
9421             var type = typeof d[ns+":"+name];
9422             if(type != 'undefined' && type != 'unknown'){
9423                 return d[ns+":"+name];
9424             }
9425             return d[name];
9426         } : function(ns, name){
9427             var d = this.dom;
9428             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9429         }
9430     };
9431
9432     var ep = El.prototype;
9433
9434     /**
9435      * Appends an event handler (Shorthand for addListener)
9436      * @param {String}   eventName     The type of event to append
9437      * @param {Function} fn        The method the event invokes
9438      * @param {Object} scope       (optional) The scope (this object) of the fn
9439      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9440      * @method
9441      */
9442     ep.on = ep.addListener;
9443         // backwards compat
9444     ep.mon = ep.addListener;
9445
9446     /**
9447      * Removes an event handler from this element (shorthand for removeListener)
9448      * @param {String} eventName the type of event to remove
9449      * @param {Function} fn the method the event invokes
9450      * @return {Roo.Element} this
9451      * @method
9452      */
9453     ep.un = ep.removeListener;
9454
9455     /**
9456      * true to automatically adjust width and height settings for box-model issues (default to true)
9457      */
9458     ep.autoBoxAdjust = true;
9459
9460     // private
9461     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9462
9463     // private
9464     El.addUnits = function(v, defaultUnit){
9465         if(v === "" || v == "auto"){
9466             return v;
9467         }
9468         if(v === undefined){
9469             return '';
9470         }
9471         if(typeof v == "number" || !El.unitPattern.test(v)){
9472             return v + (defaultUnit || 'px');
9473         }
9474         return v;
9475     };
9476
9477     // special markup used throughout Roo when box wrapping elements
9478     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>';
9479     /**
9480      * Visibility mode constant - Use visibility to hide element
9481      * @static
9482      * @type Number
9483      */
9484     El.VISIBILITY = 1;
9485     /**
9486      * Visibility mode constant - Use display to hide element
9487      * @static
9488      * @type Number
9489      */
9490     El.DISPLAY = 2;
9491
9492     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9493     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9494     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9495
9496
9497
9498     /**
9499      * @private
9500      */
9501     El.cache = {};
9502
9503     var docEl;
9504
9505     /**
9506      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9507      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9508      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9509      * @return {Element} The Element object
9510      * @static
9511      */
9512     El.get = function(el){
9513         var ex, elm, id;
9514         if(!el){ return null; }
9515         if(typeof el == "string"){ // element id
9516             if(!(elm = document.getElementById(el))){
9517                 return null;
9518             }
9519             if(ex = El.cache[el]){
9520                 ex.dom = elm;
9521             }else{
9522                 ex = El.cache[el] = new El(elm);
9523             }
9524             return ex;
9525         }else if(el.tagName){ // dom element
9526             if(!(id = el.id)){
9527                 id = Roo.id(el);
9528             }
9529             if(ex = El.cache[id]){
9530                 ex.dom = el;
9531             }else{
9532                 ex = El.cache[id] = new El(el);
9533             }
9534             return ex;
9535         }else if(el instanceof El){
9536             if(el != docEl){
9537                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9538                                                               // catch case where it hasn't been appended
9539                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9540             }
9541             return el;
9542         }else if(el.isComposite){
9543             return el;
9544         }else if(el instanceof Array){
9545             return El.select(el);
9546         }else if(el == document){
9547             // create a bogus element object representing the document object
9548             if(!docEl){
9549                 var f = function(){};
9550                 f.prototype = El.prototype;
9551                 docEl = new f();
9552                 docEl.dom = document;
9553             }
9554             return docEl;
9555         }
9556         return null;
9557     };
9558
9559     // private
9560     El.uncache = function(el){
9561         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9562             if(a[i]){
9563                 delete El.cache[a[i].id || a[i]];
9564             }
9565         }
9566     };
9567
9568     // private
9569     // Garbage collection - uncache elements/purge listeners on orphaned elements
9570     // so we don't hold a reference and cause the browser to retain them
9571     El.garbageCollect = function(){
9572         if(!Roo.enableGarbageCollector){
9573             clearInterval(El.collectorThread);
9574             return;
9575         }
9576         for(var eid in El.cache){
9577             var el = El.cache[eid], d = el.dom;
9578             // -------------------------------------------------------
9579             // Determining what is garbage:
9580             // -------------------------------------------------------
9581             // !d
9582             // dom node is null, definitely garbage
9583             // -------------------------------------------------------
9584             // !d.parentNode
9585             // no parentNode == direct orphan, definitely garbage
9586             // -------------------------------------------------------
9587             // !d.offsetParent && !document.getElementById(eid)
9588             // display none elements have no offsetParent so we will
9589             // also try to look it up by it's id. However, check
9590             // offsetParent first so we don't do unneeded lookups.
9591             // This enables collection of elements that are not orphans
9592             // directly, but somewhere up the line they have an orphan
9593             // parent.
9594             // -------------------------------------------------------
9595             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9596                 delete El.cache[eid];
9597                 if(d && Roo.enableListenerCollection){
9598                     E.purgeElement(d);
9599                 }
9600             }
9601         }
9602     }
9603     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9604
9605
9606     // dom is optional
9607     El.Flyweight = function(dom){
9608         this.dom = dom;
9609     };
9610     El.Flyweight.prototype = El.prototype;
9611
9612     El._flyweights = {};
9613     /**
9614      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9615      * the dom node can be overwritten by other code.
9616      * @param {String/HTMLElement} el The dom node or id
9617      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9618      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9619      * @static
9620      * @return {Element} The shared Element object
9621      */
9622     El.fly = function(el, named){
9623         named = named || '_global';
9624         el = Roo.getDom(el);
9625         if(!el){
9626             return null;
9627         }
9628         if(!El._flyweights[named]){
9629             El._flyweights[named] = new El.Flyweight();
9630         }
9631         El._flyweights[named].dom = el;
9632         return El._flyweights[named];
9633     };
9634
9635     /**
9636      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9637      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9638      * Shorthand of {@link Roo.Element#get}
9639      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9640      * @return {Element} The Element object
9641      * @member Roo
9642      * @method get
9643      */
9644     Roo.get = El.get;
9645     /**
9646      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9647      * the dom node can be overwritten by other code.
9648      * Shorthand of {@link Roo.Element#fly}
9649      * @param {String/HTMLElement} el The dom node or id
9650      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9651      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9652      * @static
9653      * @return {Element} The shared Element object
9654      * @member Roo
9655      * @method fly
9656      */
9657     Roo.fly = El.fly;
9658
9659     // speedy lookup for elements never to box adjust
9660     var noBoxAdjust = Roo.isStrict ? {
9661         select:1
9662     } : {
9663         input:1, select:1, textarea:1
9664     };
9665     if(Roo.isIE || Roo.isGecko){
9666         noBoxAdjust['button'] = 1;
9667     }
9668
9669
9670     Roo.EventManager.on(window, 'unload', function(){
9671         delete El.cache;
9672         delete El._flyweights;
9673     });
9674 })();
9675
9676
9677
9678
9679 if(Roo.DomQuery){
9680     Roo.Element.selectorFunction = Roo.DomQuery.select;
9681 }
9682
9683 Roo.Element.select = function(selector, unique, root){
9684     var els;
9685     if(typeof selector == "string"){
9686         els = Roo.Element.selectorFunction(selector, root);
9687     }else if(selector.length !== undefined){
9688         els = selector;
9689     }else{
9690         throw "Invalid selector";
9691     }
9692     if(unique === true){
9693         return new Roo.CompositeElement(els);
9694     }else{
9695         return new Roo.CompositeElementLite(els);
9696     }
9697 };
9698 /**
9699  * Selects elements based on the passed CSS selector to enable working on them as 1.
9700  * @param {String/Array} selector The CSS selector or an array of elements
9701  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9702  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9703  * @return {CompositeElementLite/CompositeElement}
9704  * @member Roo
9705  * @method select
9706  */
9707 Roo.select = Roo.Element.select;
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722 /*
9723  * Based on:
9724  * Ext JS Library 1.1.1
9725  * Copyright(c) 2006-2007, Ext JS, LLC.
9726  *
9727  * Originally Released Under LGPL - original licence link has changed is not relivant.
9728  *
9729  * Fork - LGPL
9730  * <script type="text/javascript">
9731  */
9732
9733
9734
9735 //Notifies Element that fx methods are available
9736 Roo.enableFx = true;
9737
9738 /**
9739  * @class Roo.Fx
9740  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9741  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9742  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9743  * Element effects to work.</p><br/>
9744  *
9745  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9746  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9747  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9748  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9749  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9750  * expected results and should be done with care.</p><br/>
9751  *
9752  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9753  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9754 <pre>
9755 Value  Description
9756 -----  -----------------------------
9757 tl     The top left corner
9758 t      The center of the top edge
9759 tr     The top right corner
9760 l      The center of the left edge
9761 r      The center of the right edge
9762 bl     The bottom left corner
9763 b      The center of the bottom edge
9764 br     The bottom right corner
9765 </pre>
9766  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9767  * below are common options that can be passed to any Fx method.</b>
9768  * @cfg {Function} callback A function called when the effect is finished
9769  * @cfg {Object} scope The scope of the effect function
9770  * @cfg {String} easing A valid Easing value for the effect
9771  * @cfg {String} afterCls A css class to apply after the effect
9772  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9773  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9774  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9775  * effects that end with the element being visually hidden, ignored otherwise)
9776  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9777  * a function which returns such a specification that will be applied to the Element after the effect finishes
9778  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9779  * @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
9780  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9781  */
9782 Roo.Fx = {
9783         /**
9784          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9785          * origin for the slide effect.  This function automatically handles wrapping the element with
9786          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9787          * Usage:
9788          *<pre><code>
9789 // default: slide the element in from the top
9790 el.slideIn();
9791
9792 // custom: slide the element in from the right with a 2-second duration
9793 el.slideIn('r', { duration: 2 });
9794
9795 // common config options shown with default values
9796 el.slideIn('t', {
9797     easing: 'easeOut',
9798     duration: .5
9799 });
9800 </code></pre>
9801          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9802          * @param {Object} options (optional) Object literal with any of the Fx config options
9803          * @return {Roo.Element} The Element
9804          */
9805     slideIn : function(anchor, o){
9806         var el = this.getFxEl();
9807         o = o || {};
9808
9809         el.queueFx(o, function(){
9810
9811             anchor = anchor || "t";
9812
9813             // fix display to visibility
9814             this.fixDisplay();
9815
9816             // restore values after effect
9817             var r = this.getFxRestore();
9818             var b = this.getBox();
9819             // fixed size for slide
9820             this.setSize(b);
9821
9822             // wrap if needed
9823             var wrap = this.fxWrap(r.pos, o, "hidden");
9824
9825             var st = this.dom.style;
9826             st.visibility = "visible";
9827             st.position = "absolute";
9828
9829             // clear out temp styles after slide and unwrap
9830             var after = function(){
9831                 el.fxUnwrap(wrap, r.pos, o);
9832                 st.width = r.width;
9833                 st.height = r.height;
9834                 el.afterFx(o);
9835             };
9836             // time to calc the positions
9837             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9838
9839             switch(anchor.toLowerCase()){
9840                 case "t":
9841                     wrap.setSize(b.width, 0);
9842                     st.left = st.bottom = "0";
9843                     a = {height: bh};
9844                 break;
9845                 case "l":
9846                     wrap.setSize(0, b.height);
9847                     st.right = st.top = "0";
9848                     a = {width: bw};
9849                 break;
9850                 case "r":
9851                     wrap.setSize(0, b.height);
9852                     wrap.setX(b.right);
9853                     st.left = st.top = "0";
9854                     a = {width: bw, points: pt};
9855                 break;
9856                 case "b":
9857                     wrap.setSize(b.width, 0);
9858                     wrap.setY(b.bottom);
9859                     st.left = st.top = "0";
9860                     a = {height: bh, points: pt};
9861                 break;
9862                 case "tl":
9863                     wrap.setSize(0, 0);
9864                     st.right = st.bottom = "0";
9865                     a = {width: bw, height: bh};
9866                 break;
9867                 case "bl":
9868                     wrap.setSize(0, 0);
9869                     wrap.setY(b.y+b.height);
9870                     st.right = st.top = "0";
9871                     a = {width: bw, height: bh, points: pt};
9872                 break;
9873                 case "br":
9874                     wrap.setSize(0, 0);
9875                     wrap.setXY([b.right, b.bottom]);
9876                     st.left = st.top = "0";
9877                     a = {width: bw, height: bh, points: pt};
9878                 break;
9879                 case "tr":
9880                     wrap.setSize(0, 0);
9881                     wrap.setX(b.x+b.width);
9882                     st.left = st.bottom = "0";
9883                     a = {width: bw, height: bh, points: pt};
9884                 break;
9885             }
9886             this.dom.style.visibility = "visible";
9887             wrap.show();
9888
9889             arguments.callee.anim = wrap.fxanim(a,
9890                 o,
9891                 'motion',
9892                 .5,
9893                 'easeOut', after);
9894         });
9895         return this;
9896     },
9897     
9898         /**
9899          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9900          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9901          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9902          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9903          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9904          * Usage:
9905          *<pre><code>
9906 // default: slide the element out to the top
9907 el.slideOut();
9908
9909 // custom: slide the element out to the right with a 2-second duration
9910 el.slideOut('r', { duration: 2 });
9911
9912 // common config options shown with default values
9913 el.slideOut('t', {
9914     easing: 'easeOut',
9915     duration: .5,
9916     remove: false,
9917     useDisplay: false
9918 });
9919 </code></pre>
9920          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9921          * @param {Object} options (optional) Object literal with any of the Fx config options
9922          * @return {Roo.Element} The Element
9923          */
9924     slideOut : function(anchor, o){
9925         var el = this.getFxEl();
9926         o = o || {};
9927
9928         el.queueFx(o, function(){
9929
9930             anchor = anchor || "t";
9931
9932             // restore values after effect
9933             var r = this.getFxRestore();
9934             
9935             var b = this.getBox();
9936             // fixed size for slide
9937             this.setSize(b);
9938
9939             // wrap if needed
9940             var wrap = this.fxWrap(r.pos, o, "visible");
9941
9942             var st = this.dom.style;
9943             st.visibility = "visible";
9944             st.position = "absolute";
9945
9946             wrap.setSize(b);
9947
9948             var after = function(){
9949                 if(o.useDisplay){
9950                     el.setDisplayed(false);
9951                 }else{
9952                     el.hide();
9953                 }
9954
9955                 el.fxUnwrap(wrap, r.pos, o);
9956
9957                 st.width = r.width;
9958                 st.height = r.height;
9959
9960                 el.afterFx(o);
9961             };
9962
9963             var a, zero = {to: 0};
9964             switch(anchor.toLowerCase()){
9965                 case "t":
9966                     st.left = st.bottom = "0";
9967                     a = {height: zero};
9968                 break;
9969                 case "l":
9970                     st.right = st.top = "0";
9971                     a = {width: zero};
9972                 break;
9973                 case "r":
9974                     st.left = st.top = "0";
9975                     a = {width: zero, points: {to:[b.right, b.y]}};
9976                 break;
9977                 case "b":
9978                     st.left = st.top = "0";
9979                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9980                 break;
9981                 case "tl":
9982                     st.right = st.bottom = "0";
9983                     a = {width: zero, height: zero};
9984                 break;
9985                 case "bl":
9986                     st.right = st.top = "0";
9987                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9988                 break;
9989                 case "br":
9990                     st.left = st.top = "0";
9991                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9992                 break;
9993                 case "tr":
9994                     st.left = st.bottom = "0";
9995                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9996                 break;
9997             }
9998
9999             arguments.callee.anim = wrap.fxanim(a,
10000                 o,
10001                 'motion',
10002                 .5,
10003                 "easeOut", after);
10004         });
10005         return this;
10006     },
10007
10008         /**
10009          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10010          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10011          * The element must be removed from the DOM using the 'remove' config option if desired.
10012          * Usage:
10013          *<pre><code>
10014 // default
10015 el.puff();
10016
10017 // common config options shown with default values
10018 el.puff({
10019     easing: 'easeOut',
10020     duration: .5,
10021     remove: false,
10022     useDisplay: false
10023 });
10024 </code></pre>
10025          * @param {Object} options (optional) Object literal with any of the Fx config options
10026          * @return {Roo.Element} The Element
10027          */
10028     puff : function(o){
10029         var el = this.getFxEl();
10030         o = o || {};
10031
10032         el.queueFx(o, function(){
10033             this.clearOpacity();
10034             this.show();
10035
10036             // restore values after effect
10037             var r = this.getFxRestore();
10038             var st = this.dom.style;
10039
10040             var after = function(){
10041                 if(o.useDisplay){
10042                     el.setDisplayed(false);
10043                 }else{
10044                     el.hide();
10045                 }
10046
10047                 el.clearOpacity();
10048
10049                 el.setPositioning(r.pos);
10050                 st.width = r.width;
10051                 st.height = r.height;
10052                 st.fontSize = '';
10053                 el.afterFx(o);
10054             };
10055
10056             var width = this.getWidth();
10057             var height = this.getHeight();
10058
10059             arguments.callee.anim = this.fxanim({
10060                     width : {to: this.adjustWidth(width * 2)},
10061                     height : {to: this.adjustHeight(height * 2)},
10062                     points : {by: [-(width * .5), -(height * .5)]},
10063                     opacity : {to: 0},
10064                     fontSize: {to:200, unit: "%"}
10065                 },
10066                 o,
10067                 'motion',
10068                 .5,
10069                 "easeOut", after);
10070         });
10071         return this;
10072     },
10073
10074         /**
10075          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10076          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10077          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10078          * Usage:
10079          *<pre><code>
10080 // default
10081 el.switchOff();
10082
10083 // all config options shown with default values
10084 el.switchOff({
10085     easing: 'easeIn',
10086     duration: .3,
10087     remove: false,
10088     useDisplay: false
10089 });
10090 </code></pre>
10091          * @param {Object} options (optional) Object literal with any of the Fx config options
10092          * @return {Roo.Element} The Element
10093          */
10094     switchOff : function(o){
10095         var el = this.getFxEl();
10096         o = o || {};
10097
10098         el.queueFx(o, function(){
10099             this.clearOpacity();
10100             this.clip();
10101
10102             // restore values after effect
10103             var r = this.getFxRestore();
10104             var st = this.dom.style;
10105
10106             var after = function(){
10107                 if(o.useDisplay){
10108                     el.setDisplayed(false);
10109                 }else{
10110                     el.hide();
10111                 }
10112
10113                 el.clearOpacity();
10114                 el.setPositioning(r.pos);
10115                 st.width = r.width;
10116                 st.height = r.height;
10117
10118                 el.afterFx(o);
10119             };
10120
10121             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10122                 this.clearOpacity();
10123                 (function(){
10124                     this.fxanim({
10125                         height:{to:1},
10126                         points:{by:[0, this.getHeight() * .5]}
10127                     }, o, 'motion', 0.3, 'easeIn', after);
10128                 }).defer(100, this);
10129             });
10130         });
10131         return this;
10132     },
10133
10134     /**
10135      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10136      * changed using the "attr" config option) and then fading back to the original color. If no original
10137      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10138      * Usage:
10139 <pre><code>
10140 // default: highlight background to yellow
10141 el.highlight();
10142
10143 // custom: highlight foreground text to blue for 2 seconds
10144 el.highlight("0000ff", { attr: 'color', duration: 2 });
10145
10146 // common config options shown with default values
10147 el.highlight("ffff9c", {
10148     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10149     endColor: (current color) or "ffffff",
10150     easing: 'easeIn',
10151     duration: 1
10152 });
10153 </code></pre>
10154      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10155      * @param {Object} options (optional) Object literal with any of the Fx config options
10156      * @return {Roo.Element} The Element
10157      */ 
10158     highlight : function(color, o){
10159         var el = this.getFxEl();
10160         o = o || {};
10161
10162         el.queueFx(o, function(){
10163             color = color || "ffff9c";
10164             attr = o.attr || "backgroundColor";
10165
10166             this.clearOpacity();
10167             this.show();
10168
10169             var origColor = this.getColor(attr);
10170             var restoreColor = this.dom.style[attr];
10171             endColor = (o.endColor || origColor) || "ffffff";
10172
10173             var after = function(){
10174                 el.dom.style[attr] = restoreColor;
10175                 el.afterFx(o);
10176             };
10177
10178             var a = {};
10179             a[attr] = {from: color, to: endColor};
10180             arguments.callee.anim = this.fxanim(a,
10181                 o,
10182                 'color',
10183                 1,
10184                 'easeIn', after);
10185         });
10186         return this;
10187     },
10188
10189    /**
10190     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10191     * Usage:
10192 <pre><code>
10193 // default: a single light blue ripple
10194 el.frame();
10195
10196 // custom: 3 red ripples lasting 3 seconds total
10197 el.frame("ff0000", 3, { duration: 3 });
10198
10199 // common config options shown with default values
10200 el.frame("C3DAF9", 1, {
10201     duration: 1 //duration of entire animation (not each individual ripple)
10202     // Note: Easing is not configurable and will be ignored if included
10203 });
10204 </code></pre>
10205     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10206     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10207     * @param {Object} options (optional) Object literal with any of the Fx config options
10208     * @return {Roo.Element} The Element
10209     */
10210     frame : function(color, count, o){
10211         var el = this.getFxEl();
10212         o = o || {};
10213
10214         el.queueFx(o, function(){
10215             color = color || "#C3DAF9";
10216             if(color.length == 6){
10217                 color = "#" + color;
10218             }
10219             count = count || 1;
10220             duration = o.duration || 1;
10221             this.show();
10222
10223             var b = this.getBox();
10224             var animFn = function(){
10225                 var proxy = this.createProxy({
10226
10227                      style:{
10228                         visbility:"hidden",
10229                         position:"absolute",
10230                         "z-index":"35000", // yee haw
10231                         border:"0px solid " + color
10232                      }
10233                   });
10234                 var scale = Roo.isBorderBox ? 2 : 1;
10235                 proxy.animate({
10236                     top:{from:b.y, to:b.y - 20},
10237                     left:{from:b.x, to:b.x - 20},
10238                     borderWidth:{from:0, to:10},
10239                     opacity:{from:1, to:0},
10240                     height:{from:b.height, to:(b.height + (20*scale))},
10241                     width:{from:b.width, to:(b.width + (20*scale))}
10242                 }, duration, function(){
10243                     proxy.remove();
10244                 });
10245                 if(--count > 0){
10246                      animFn.defer((duration/2)*1000, this);
10247                 }else{
10248                     el.afterFx(o);
10249                 }
10250             };
10251             animFn.call(this);
10252         });
10253         return this;
10254     },
10255
10256    /**
10257     * Creates a pause before any subsequent queued effects begin.  If there are
10258     * no effects queued after the pause it will have no effect.
10259     * Usage:
10260 <pre><code>
10261 el.pause(1);
10262 </code></pre>
10263     * @param {Number} seconds The length of time to pause (in seconds)
10264     * @return {Roo.Element} The Element
10265     */
10266     pause : function(seconds){
10267         var el = this.getFxEl();
10268         var o = {};
10269
10270         el.queueFx(o, function(){
10271             setTimeout(function(){
10272                 el.afterFx(o);
10273             }, seconds * 1000);
10274         });
10275         return this;
10276     },
10277
10278    /**
10279     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10280     * using the "endOpacity" config option.
10281     * Usage:
10282 <pre><code>
10283 // default: fade in from opacity 0 to 100%
10284 el.fadeIn();
10285
10286 // custom: fade in from opacity 0 to 75% over 2 seconds
10287 el.fadeIn({ endOpacity: .75, duration: 2});
10288
10289 // common config options shown with default values
10290 el.fadeIn({
10291     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10292     easing: 'easeOut',
10293     duration: .5
10294 });
10295 </code></pre>
10296     * @param {Object} options (optional) Object literal with any of the Fx config options
10297     * @return {Roo.Element} The Element
10298     */
10299     fadeIn : function(o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302         el.queueFx(o, function(){
10303             this.setOpacity(0);
10304             this.fixDisplay();
10305             this.dom.style.visibility = 'visible';
10306             var to = o.endOpacity || 1;
10307             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10308                 o, null, .5, "easeOut", function(){
10309                 if(to == 1){
10310                     this.clearOpacity();
10311                 }
10312                 el.afterFx(o);
10313             });
10314         });
10315         return this;
10316     },
10317
10318    /**
10319     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10320     * using the "endOpacity" config option.
10321     * Usage:
10322 <pre><code>
10323 // default: fade out from the element's current opacity to 0
10324 el.fadeOut();
10325
10326 // custom: fade out from the element's current opacity to 25% over 2 seconds
10327 el.fadeOut({ endOpacity: .25, duration: 2});
10328
10329 // common config options shown with default values
10330 el.fadeOut({
10331     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10332     easing: 'easeOut',
10333     duration: .5
10334     remove: false,
10335     useDisplay: false
10336 });
10337 </code></pre>
10338     * @param {Object} options (optional) Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     fadeOut : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10346                 o, null, .5, "easeOut", function(){
10347                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10348                      this.dom.style.display = "none";
10349                 }else{
10350                      this.dom.style.visibility = "hidden";
10351                 }
10352                 this.clearOpacity();
10353                 el.afterFx(o);
10354             });
10355         });
10356         return this;
10357     },
10358
10359    /**
10360     * Animates the transition of an element's dimensions from a starting height/width
10361     * to an ending height/width.
10362     * Usage:
10363 <pre><code>
10364 // change height and width to 100x100 pixels
10365 el.scale(100, 100);
10366
10367 // common config options shown with default values.  The height and width will default to
10368 // the element's existing values if passed as null.
10369 el.scale(
10370     [element's width],
10371     [element's height], {
10372     easing: 'easeOut',
10373     duration: .35
10374 });
10375 </code></pre>
10376     * @param {Number} width  The new width (pass undefined to keep the original width)
10377     * @param {Number} height  The new height (pass undefined to keep the original height)
10378     * @param {Object} options (optional) Object literal with any of the Fx config options
10379     * @return {Roo.Element} The Element
10380     */
10381     scale : function(w, h, o){
10382         this.shift(Roo.apply({}, o, {
10383             width: w,
10384             height: h
10385         }));
10386         return this;
10387     },
10388
10389    /**
10390     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10391     * Any of these properties not specified in the config object will not be changed.  This effect 
10392     * requires that at least one new dimension, position or opacity setting must be passed in on
10393     * the config object in order for the function to have any effect.
10394     * Usage:
10395 <pre><code>
10396 // slide the element horizontally to x position 200 while changing the height and opacity
10397 el.shift({ x: 200, height: 50, opacity: .8 });
10398
10399 // common config options shown with default values.
10400 el.shift({
10401     width: [element's width],
10402     height: [element's height],
10403     x: [element's x position],
10404     y: [element's y position],
10405     opacity: [element's opacity],
10406     easing: 'easeOut',
10407     duration: .35
10408 });
10409 </code></pre>
10410     * @param {Object} options  Object literal with any of the Fx config options
10411     * @return {Roo.Element} The Element
10412     */
10413     shift : function(o){
10414         var el = this.getFxEl();
10415         o = o || {};
10416         el.queueFx(o, function(){
10417             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10418             if(w !== undefined){
10419                 a.width = {to: this.adjustWidth(w)};
10420             }
10421             if(h !== undefined){
10422                 a.height = {to: this.adjustHeight(h)};
10423             }
10424             if(x !== undefined || y !== undefined){
10425                 a.points = {to: [
10426                     x !== undefined ? x : this.getX(),
10427                     y !== undefined ? y : this.getY()
10428                 ]};
10429             }
10430             if(op !== undefined){
10431                 a.opacity = {to: op};
10432             }
10433             if(o.xy !== undefined){
10434                 a.points = {to: o.xy};
10435             }
10436             arguments.callee.anim = this.fxanim(a,
10437                 o, 'motion', .35, "easeOut", function(){
10438                 el.afterFx(o);
10439             });
10440         });
10441         return this;
10442     },
10443
10444         /**
10445          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10446          * ending point of the effect.
10447          * Usage:
10448          *<pre><code>
10449 // default: slide the element downward while fading out
10450 el.ghost();
10451
10452 // custom: slide the element out to the right with a 2-second duration
10453 el.ghost('r', { duration: 2 });
10454
10455 // common config options shown with default values
10456 el.ghost('b', {
10457     easing: 'easeOut',
10458     duration: .5
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     ghost : function(anchor, o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             anchor = anchor || "b";
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var w = this.getWidth(),
10477                 h = this.getHeight();
10478
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10497             switch(anchor.toLowerCase()){
10498                 case "t":
10499                     pt.by = [0, -h];
10500                 break;
10501                 case "l":
10502                     pt.by = [-w, 0];
10503                 break;
10504                 case "r":
10505                     pt.by = [w, 0];
10506                 break;
10507                 case "b":
10508                     pt.by = [0, h];
10509                 break;
10510                 case "tl":
10511                     pt.by = [-w, -h];
10512                 break;
10513                 case "bl":
10514                     pt.by = [-w, h];
10515                 break;
10516                 case "br":
10517                     pt.by = [w, h];
10518                 break;
10519                 case "tr":
10520                     pt.by = [w, -h];
10521                 break;
10522             }
10523
10524             arguments.callee.anim = this.fxanim(a,
10525                 o,
10526                 'motion',
10527                 .5,
10528                 "easeOut", after);
10529         });
10530         return this;
10531     },
10532
10533         /**
10534          * Ensures that all effects queued after syncFx is called on the element are
10535          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10536          * @return {Roo.Element} The Element
10537          */
10538     syncFx : function(){
10539         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10540             block : false,
10541             concurrent : true,
10542             stopFx : false
10543         });
10544         return this;
10545     },
10546
10547         /**
10548          * Ensures that all effects queued after sequenceFx is called on the element are
10549          * run in sequence.  This is the opposite of {@link #syncFx}.
10550          * @return {Roo.Element} The Element
10551          */
10552     sequenceFx : function(){
10553         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10554             block : false,
10555             concurrent : false,
10556             stopFx : false
10557         });
10558         return this;
10559     },
10560
10561         /* @private */
10562     nextFx : function(){
10563         var ef = this.fxQueue[0];
10564         if(ef){
10565             ef.call(this);
10566         }
10567     },
10568
10569         /**
10570          * Returns true if the element has any effects actively running or queued, else returns false.
10571          * @return {Boolean} True if element has active effects, else false
10572          */
10573     hasActiveFx : function(){
10574         return this.fxQueue && this.fxQueue[0];
10575     },
10576
10577         /**
10578          * Stops any running effects and clears the element's internal effects queue if it contains
10579          * any additional effects that haven't started yet.
10580          * @return {Roo.Element} The Element
10581          */
10582     stopFx : function(){
10583         if(this.hasActiveFx()){
10584             var cur = this.fxQueue[0];
10585             if(cur && cur.anim && cur.anim.isAnimated()){
10586                 this.fxQueue = [cur]; // clear out others
10587                 cur.anim.stop(true);
10588             }
10589         }
10590         return this;
10591     },
10592
10593         /* @private */
10594     beforeFx : function(o){
10595         if(this.hasActiveFx() && !o.concurrent){
10596            if(o.stopFx){
10597                this.stopFx();
10598                return true;
10599            }
10600            return false;
10601         }
10602         return true;
10603     },
10604
10605         /**
10606          * Returns true if the element is currently blocking so that no other effect can be queued
10607          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10608          * used to ensure that an effect initiated by a user action runs to completion prior to the
10609          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10610          * @return {Boolean} True if blocking, else false
10611          */
10612     hasFxBlock : function(){
10613         var q = this.fxQueue;
10614         return q && q[0] && q[0].block;
10615     },
10616
10617         /* @private */
10618     queueFx : function(o, fn){
10619         if(!this.fxQueue){
10620             this.fxQueue = [];
10621         }
10622         if(!this.hasFxBlock()){
10623             Roo.applyIf(o, this.fxDefaults);
10624             if(!o.concurrent){
10625                 var run = this.beforeFx(o);
10626                 fn.block = o.block;
10627                 this.fxQueue.push(fn);
10628                 if(run){
10629                     this.nextFx();
10630                 }
10631             }else{
10632                 fn.call(this);
10633             }
10634         }
10635         return this;
10636     },
10637
10638         /* @private */
10639     fxWrap : function(pos, o, vis){
10640         var wrap;
10641         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10642             var wrapXY;
10643             if(o.fixPosition){
10644                 wrapXY = this.getXY();
10645             }
10646             var div = document.createElement("div");
10647             div.style.visibility = vis;
10648             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10649             wrap.setPositioning(pos);
10650             if(wrap.getStyle("position") == "static"){
10651                 wrap.position("relative");
10652             }
10653             this.clearPositioning('auto');
10654             wrap.clip();
10655             wrap.dom.appendChild(this.dom);
10656             if(wrapXY){
10657                 wrap.setXY(wrapXY);
10658             }
10659         }
10660         return wrap;
10661     },
10662
10663         /* @private */
10664     fxUnwrap : function(wrap, pos, o){
10665         this.clearPositioning();
10666         this.setPositioning(pos);
10667         if(!o.wrap){
10668             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10669             wrap.remove();
10670         }
10671     },
10672
10673         /* @private */
10674     getFxRestore : function(){
10675         var st = this.dom.style;
10676         return {pos: this.getPositioning(), width: st.width, height : st.height};
10677     },
10678
10679         /* @private */
10680     afterFx : function(o){
10681         if(o.afterStyle){
10682             this.applyStyles(o.afterStyle);
10683         }
10684         if(o.afterCls){
10685             this.addClass(o.afterCls);
10686         }
10687         if(o.remove === true){
10688             this.remove();
10689         }
10690         Roo.callback(o.callback, o.scope, [this]);
10691         if(!o.concurrent){
10692             this.fxQueue.shift();
10693             this.nextFx();
10694         }
10695     },
10696
10697         /* @private */
10698     getFxEl : function(){ // support for composite element fx
10699         return Roo.get(this.dom);
10700     },
10701
10702         /* @private */
10703     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10704         animType = animType || 'run';
10705         opt = opt || {};
10706         var anim = Roo.lib.Anim[animType](
10707             this.dom, args,
10708             (opt.duration || defaultDur) || .35,
10709             (opt.easing || defaultEase) || 'easeOut',
10710             function(){
10711                 Roo.callback(cb, this);
10712             },
10713             this
10714         );
10715         opt.anim = anim;
10716         return anim;
10717     }
10718 };
10719
10720 // backwords compat
10721 Roo.Fx.resize = Roo.Fx.scale;
10722
10723 //When included, Roo.Fx is automatically applied to Element so that all basic
10724 //effects are available directly via the Element API
10725 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10726  * Based on:
10727  * Ext JS Library 1.1.1
10728  * Copyright(c) 2006-2007, Ext JS, LLC.
10729  *
10730  * Originally Released Under LGPL - original licence link has changed is not relivant.
10731  *
10732  * Fork - LGPL
10733  * <script type="text/javascript">
10734  */
10735
10736
10737 /**
10738  * @class Roo.CompositeElement
10739  * Standard composite class. Creates a Roo.Element for every element in the collection.
10740  * <br><br>
10741  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10742  * actions will be performed on all the elements in this collection.</b>
10743  * <br><br>
10744  * All methods return <i>this</i> and can be chained.
10745  <pre><code>
10746  var els = Roo.select("#some-el div.some-class", true);
10747  // or select directly from an existing element
10748  var el = Roo.get('some-el');
10749  el.select('div.some-class', true);
10750
10751  els.setWidth(100); // all elements become 100 width
10752  els.hide(true); // all elements fade out and hide
10753  // or
10754  els.setWidth(100).hide(true);
10755  </code></pre>
10756  */
10757 Roo.CompositeElement = function(els){
10758     this.elements = [];
10759     this.addElements(els);
10760 };
10761 Roo.CompositeElement.prototype = {
10762     isComposite: true,
10763     addElements : function(els){
10764         if(!els) return this;
10765         if(typeof els == "string"){
10766             els = Roo.Element.selectorFunction(els);
10767         }
10768         var yels = this.elements;
10769         var index = yels.length-1;
10770         for(var i = 0, len = els.length; i < len; i++) {
10771                 yels[++index] = Roo.get(els[i]);
10772         }
10773         return this;
10774     },
10775
10776     /**
10777     * Clears this composite and adds the elements returned by the passed selector.
10778     * @param {String/Array} els A string CSS selector, an array of elements or an element
10779     * @return {CompositeElement} this
10780     */
10781     fill : function(els){
10782         this.elements = [];
10783         this.add(els);
10784         return this;
10785     },
10786
10787     /**
10788     * Filters this composite to only elements that match the passed selector.
10789     * @param {String} selector A string CSS selector
10790     * @return {CompositeElement} this
10791     */
10792     filter : function(selector){
10793         var els = [];
10794         this.each(function(el){
10795             if(el.is(selector)){
10796                 els[els.length] = el.dom;
10797             }
10798         });
10799         this.fill(els);
10800         return this;
10801     },
10802
10803     invoke : function(fn, args){
10804         var els = this.elements;
10805         for(var i = 0, len = els.length; i < len; i++) {
10806                 Roo.Element.prototype[fn].apply(els[i], args);
10807         }
10808         return this;
10809     },
10810     /**
10811     * Adds elements to this composite.
10812     * @param {String/Array} els A string CSS selector, an array of elements or an element
10813     * @return {CompositeElement} this
10814     */
10815     add : function(els){
10816         if(typeof els == "string"){
10817             this.addElements(Roo.Element.selectorFunction(els));
10818         }else if(els.length !== undefined){
10819             this.addElements(els);
10820         }else{
10821             this.addElements([els]);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Calls the passed function passing (el, this, index) for each element in this composite.
10827     * @param {Function} fn The function to call
10828     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10829     * @return {CompositeElement} this
10830     */
10831     each : function(fn, scope){
10832         var els = this.elements;
10833         for(var i = 0, len = els.length; i < len; i++){
10834             if(fn.call(scope || els[i], els[i], this, i) === false) {
10835                 break;
10836             }
10837         }
10838         return this;
10839     },
10840
10841     /**
10842      * Returns the Element object at the specified index
10843      * @param {Number} index
10844      * @return {Roo.Element}
10845      */
10846     item : function(index){
10847         return this.elements[index] || null;
10848     },
10849
10850     /**
10851      * Returns the first Element
10852      * @return {Roo.Element}
10853      */
10854     first : function(){
10855         return this.item(0);
10856     },
10857
10858     /**
10859      * Returns the last Element
10860      * @return {Roo.Element}
10861      */
10862     last : function(){
10863         return this.item(this.elements.length-1);
10864     },
10865
10866     /**
10867      * Returns the number of elements in this composite
10868      * @return Number
10869      */
10870     getCount : function(){
10871         return this.elements.length;
10872     },
10873
10874     /**
10875      * Returns true if this composite contains the passed element
10876      * @return Boolean
10877      */
10878     contains : function(el){
10879         return this.indexOf(el) !== -1;
10880     },
10881
10882     /**
10883      * Returns true if this composite contains the passed element
10884      * @return Boolean
10885      */
10886     indexOf : function(el){
10887         return this.elements.indexOf(Roo.get(el));
10888     },
10889
10890
10891     /**
10892     * Removes the specified element(s).
10893     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10894     * or an array of any of those.
10895     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10896     * @return {CompositeElement} this
10897     */
10898     removeElement : function(el, removeDom){
10899         if(el instanceof Array){
10900             for(var i = 0, len = el.length; i < len; i++){
10901                 this.removeElement(el[i]);
10902             }
10903             return this;
10904         }
10905         var index = typeof el == 'number' ? el : this.indexOf(el);
10906         if(index !== -1){
10907             if(removeDom){
10908                 var d = this.elements[index];
10909                 if(d.dom){
10910                     d.remove();
10911                 }else{
10912                     d.parentNode.removeChild(d);
10913                 }
10914             }
10915             this.elements.splice(index, 1);
10916         }
10917         return this;
10918     },
10919
10920     /**
10921     * Replaces the specified element with the passed element.
10922     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10923     * to replace.
10924     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10925     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10926     * @return {CompositeElement} this
10927     */
10928     replaceElement : function(el, replacement, domReplace){
10929         var index = typeof el == 'number' ? el : this.indexOf(el);
10930         if(index !== -1){
10931             if(domReplace){
10932                 this.elements[index].replaceWith(replacement);
10933             }else{
10934                 this.elements.splice(index, 1, Roo.get(replacement))
10935             }
10936         }
10937         return this;
10938     },
10939
10940     /**
10941      * Removes all elements.
10942      */
10943     clear : function(){
10944         this.elements = [];
10945     }
10946 };
10947 (function(){
10948     Roo.CompositeElement.createCall = function(proto, fnName){
10949         if(!proto[fnName]){
10950             proto[fnName] = function(){
10951                 return this.invoke(fnName, arguments);
10952             };
10953         }
10954     };
10955     for(var fnName in Roo.Element.prototype){
10956         if(typeof Roo.Element.prototype[fnName] == "function"){
10957             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10958         }
10959     };
10960 })();
10961 /*
10962  * Based on:
10963  * Ext JS Library 1.1.1
10964  * Copyright(c) 2006-2007, Ext JS, LLC.
10965  *
10966  * Originally Released Under LGPL - original licence link has changed is not relivant.
10967  *
10968  * Fork - LGPL
10969  * <script type="text/javascript">
10970  */
10971
10972 /**
10973  * @class Roo.CompositeElementLite
10974  * @extends Roo.CompositeElement
10975  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10976  <pre><code>
10977  var els = Roo.select("#some-el div.some-class");
10978  // or select directly from an existing element
10979  var el = Roo.get('some-el');
10980  el.select('div.some-class');
10981
10982  els.setWidth(100); // all elements become 100 width
10983  els.hide(true); // all elements fade out and hide
10984  // or
10985  els.setWidth(100).hide(true);
10986  </code></pre><br><br>
10987  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10988  * actions will be performed on all the elements in this collection.</b>
10989  */
10990 Roo.CompositeElementLite = function(els){
10991     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10992     this.el = new Roo.Element.Flyweight();
10993 };
10994 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10995     addElements : function(els){
10996         if(els){
10997             if(els instanceof Array){
10998                 this.elements = this.elements.concat(els);
10999             }else{
11000                 var yels = this.elements;
11001                 var index = yels.length-1;
11002                 for(var i = 0, len = els.length; i < len; i++) {
11003                     yels[++index] = els[i];
11004                 }
11005             }
11006         }
11007         return this;
11008     },
11009     invoke : function(fn, args){
11010         var els = this.elements;
11011         var el = this.el;
11012         for(var i = 0, len = els.length; i < len; i++) {
11013             el.dom = els[i];
11014                 Roo.Element.prototype[fn].apply(el, args);
11015         }
11016         return this;
11017     },
11018     /**
11019      * Returns a flyweight Element of the dom element object at the specified index
11020      * @param {Number} index
11021      * @return {Roo.Element}
11022      */
11023     item : function(index){
11024         if(!this.elements[index]){
11025             return null;
11026         }
11027         this.el.dom = this.elements[index];
11028         return this.el;
11029     },
11030
11031     // fixes scope with flyweight
11032     addListener : function(eventName, handler, scope, opt){
11033         var els = this.elements;
11034         for(var i = 0, len = els.length; i < len; i++) {
11035             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11036         }
11037         return this;
11038     },
11039
11040     /**
11041     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11042     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11043     * a reference to the dom node, use el.dom.</b>
11044     * @param {Function} fn The function to call
11045     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11046     * @return {CompositeElement} this
11047     */
11048     each : function(fn, scope){
11049         var els = this.elements;
11050         var el = this.el;
11051         for(var i = 0, len = els.length; i < len; i++){
11052             el.dom = els[i];
11053                 if(fn.call(scope || el, el, this, i) === false){
11054                 break;
11055             }
11056         }
11057         return this;
11058     },
11059
11060     indexOf : function(el){
11061         return this.elements.indexOf(Roo.getDom(el));
11062     },
11063
11064     replaceElement : function(el, replacement, domReplace){
11065         var index = typeof el == 'number' ? el : this.indexOf(el);
11066         if(index !== -1){
11067             replacement = Roo.getDom(replacement);
11068             if(domReplace){
11069                 var d = this.elements[index];
11070                 d.parentNode.insertBefore(replacement, d);
11071                 d.parentNode.removeChild(d);
11072             }
11073             this.elements.splice(index, 1, replacement);
11074         }
11075         return this;
11076     }
11077 });
11078 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11079
11080 /*
11081  * Based on:
11082  * Ext JS Library 1.1.1
11083  * Copyright(c) 2006-2007, Ext JS, LLC.
11084  *
11085  * Originally Released Under LGPL - original licence link has changed is not relivant.
11086  *
11087  * Fork - LGPL
11088  * <script type="text/javascript">
11089  */
11090
11091  
11092
11093 /**
11094  * @class Roo.data.Connection
11095  * @extends Roo.util.Observable
11096  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11097  * either to a configured URL, or to a URL specified at request time.<br><br>
11098  * <p>
11099  * Requests made by this class are asynchronous, and will return immediately. No data from
11100  * the server will be available to the statement immediately following the {@link #request} call.
11101  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11102  * <p>
11103  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11104  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11105  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11106  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11107  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11108  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11109  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11110  * standard DOM methods.
11111  * @constructor
11112  * @param {Object} config a configuration object.
11113  */
11114 Roo.data.Connection = function(config){
11115     Roo.apply(this, config);
11116     this.addEvents({
11117         /**
11118          * @event beforerequest
11119          * Fires before a network request is made to retrieve a data object.
11120          * @param {Connection} conn This Connection object.
11121          * @param {Object} options The options config object passed to the {@link #request} method.
11122          */
11123         "beforerequest" : true,
11124         /**
11125          * @event requestcomplete
11126          * Fires if the request was successfully completed.
11127          * @param {Connection} conn This Connection object.
11128          * @param {Object} response The XHR object containing the response data.
11129          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11130          * @param {Object} options The options config object passed to the {@link #request} method.
11131          */
11132         "requestcomplete" : true,
11133         /**
11134          * @event requestexception
11135          * Fires if an error HTTP status was returned from the server.
11136          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11137          * @param {Connection} conn This Connection object.
11138          * @param {Object} response The XHR object containing the response data.
11139          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11140          * @param {Object} options The options config object passed to the {@link #request} method.
11141          */
11142         "requestexception" : true
11143     });
11144     Roo.data.Connection.superclass.constructor.call(this);
11145 };
11146
11147 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11148     /**
11149      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11150      */
11151     /**
11152      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11153      * extra parameters to each request made by this object. (defaults to undefined)
11154      */
11155     /**
11156      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11157      *  to each request made by this object. (defaults to undefined)
11158      */
11159     /**
11160      * @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)
11161      */
11162     /**
11163      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11164      */
11165     timeout : 30000,
11166     /**
11167      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11168      * @type Boolean
11169      */
11170     autoAbort:false,
11171
11172     /**
11173      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11174      * @type Boolean
11175      */
11176     disableCaching: true,
11177
11178     /**
11179      * Sends an HTTP request to a remote server.
11180      * @param {Object} options An object which may contain the following properties:<ul>
11181      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11182      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11183      * request, a url encoded string or a function to call to get either.</li>
11184      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11185      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11186      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11187      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11188      * <li>options {Object} The parameter to the request call.</li>
11189      * <li>success {Boolean} True if the request succeeded.</li>
11190      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11191      * </ul></li>
11192      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11193      * The callback is passed the following parameters:<ul>
11194      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11195      * <li>options {Object} The parameter to the request call.</li>
11196      * </ul></li>
11197      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11198      * The callback is passed the following parameters:<ul>
11199      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11200      * <li>options {Object} The parameter to the request call.</li>
11201      * </ul></li>
11202      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11203      * for the callback function. Defaults to the browser window.</li>
11204      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11205      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11206      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11207      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11208      * params for the post data. Any params will be appended to the URL.</li>
11209      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11210      * </ul>
11211      * @return {Number} transactionId
11212      */
11213     request : function(o){
11214         if(this.fireEvent("beforerequest", this, o) !== false){
11215             var p = o.params;
11216
11217             if(typeof p == "function"){
11218                 p = p.call(o.scope||window, o);
11219             }
11220             if(typeof p == "object"){
11221                 p = Roo.urlEncode(o.params);
11222             }
11223             if(this.extraParams){
11224                 var extras = Roo.urlEncode(this.extraParams);
11225                 p = p ? (p + '&' + extras) : extras;
11226             }
11227
11228             var url = o.url || this.url;
11229             if(typeof url == 'function'){
11230                 url = url.call(o.scope||window, o);
11231             }
11232
11233             if(o.form){
11234                 var form = Roo.getDom(o.form);
11235                 url = url || form.action;
11236
11237                 var enctype = form.getAttribute("enctype");
11238                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11239                     return this.doFormUpload(o, p, url);
11240                 }
11241                 var f = Roo.lib.Ajax.serializeForm(form);
11242                 p = p ? (p + '&' + f) : f;
11243             }
11244
11245             var hs = o.headers;
11246             if(this.defaultHeaders){
11247                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11248                 if(!o.headers){
11249                     o.headers = hs;
11250                 }
11251             }
11252
11253             var cb = {
11254                 success: this.handleResponse,
11255                 failure: this.handleFailure,
11256                 scope: this,
11257                 argument: {options: o},
11258                 timeout : this.timeout
11259             };
11260
11261             var method = o.method||this.method||(p ? "POST" : "GET");
11262
11263             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11264                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11265             }
11266
11267             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11268                 if(o.autoAbort){
11269                     this.abort();
11270                 }
11271             }else if(this.autoAbort !== false){
11272                 this.abort();
11273             }
11274
11275             if((method == 'GET' && p) || o.xmlData){
11276                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11277                 p = '';
11278             }
11279             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11280             return this.transId;
11281         }else{
11282             Roo.callback(o.callback, o.scope, [o, null, null]);
11283             return null;
11284         }
11285     },
11286
11287     /**
11288      * Determine whether this object has a request outstanding.
11289      * @param {Number} transactionId (Optional) defaults to the last transaction
11290      * @return {Boolean} True if there is an outstanding request.
11291      */
11292     isLoading : function(transId){
11293         if(transId){
11294             return Roo.lib.Ajax.isCallInProgress(transId);
11295         }else{
11296             return this.transId ? true : false;
11297         }
11298     },
11299
11300     /**
11301      * Aborts any outstanding request.
11302      * @param {Number} transactionId (Optional) defaults to the last transaction
11303      */
11304     abort : function(transId){
11305         if(transId || this.isLoading()){
11306             Roo.lib.Ajax.abort(transId || this.transId);
11307         }
11308     },
11309
11310     // private
11311     handleResponse : function(response){
11312         this.transId = false;
11313         var options = response.argument.options;
11314         response.argument = options ? options.argument : null;
11315         this.fireEvent("requestcomplete", this, response, options);
11316         Roo.callback(options.success, options.scope, [response, options]);
11317         Roo.callback(options.callback, options.scope, [options, true, response]);
11318     },
11319
11320     // private
11321     handleFailure : function(response, e){
11322         this.transId = false;
11323         var options = response.argument.options;
11324         response.argument = options ? options.argument : null;
11325         this.fireEvent("requestexception", this, response, options, e);
11326         Roo.callback(options.failure, options.scope, [response, options]);
11327         Roo.callback(options.callback, options.scope, [options, false, response]);
11328     },
11329
11330     // private
11331     doFormUpload : function(o, ps, url){
11332         var id = Roo.id();
11333         var frame = document.createElement('iframe');
11334         frame.id = id;
11335         frame.name = id;
11336         frame.className = 'x-hidden';
11337         if(Roo.isIE){
11338             frame.src = Roo.SSL_SECURE_URL;
11339         }
11340         document.body.appendChild(frame);
11341
11342         if(Roo.isIE){
11343            document.frames[id].name = id;
11344         }
11345
11346         var form = Roo.getDom(o.form);
11347         form.target = id;
11348         form.method = 'POST';
11349         form.enctype = form.encoding = 'multipart/form-data';
11350         if(url){
11351             form.action = url;
11352         }
11353
11354         var hiddens, hd;
11355         if(ps){ // add dynamic params
11356             hiddens = [];
11357             ps = Roo.urlDecode(ps, false);
11358             for(var k in ps){
11359                 if(ps.hasOwnProperty(k)){
11360                     hd = document.createElement('input');
11361                     hd.type = 'hidden';
11362                     hd.name = k;
11363                     hd.value = ps[k];
11364                     form.appendChild(hd);
11365                     hiddens.push(hd);
11366                 }
11367             }
11368         }
11369
11370         function cb(){
11371             var r = {  // bogus response object
11372                 responseText : '',
11373                 responseXML : null
11374             };
11375
11376             r.argument = o ? o.argument : null;
11377
11378             try { //
11379                 var doc;
11380                 if(Roo.isIE){
11381                     doc = frame.contentWindow.document;
11382                 }else {
11383                     doc = (frame.contentDocument || window.frames[id].document);
11384                 }
11385                 if(doc && doc.body){
11386                     r.responseText = doc.body.innerHTML;
11387                 }
11388                 if(doc && doc.XMLDocument){
11389                     r.responseXML = doc.XMLDocument;
11390                 }else {
11391                     r.responseXML = doc;
11392                 }
11393             }
11394             catch(e) {
11395                 // ignore
11396             }
11397
11398             Roo.EventManager.removeListener(frame, 'load', cb, this);
11399
11400             this.fireEvent("requestcomplete", this, r, o);
11401             Roo.callback(o.success, o.scope, [r, o]);
11402             Roo.callback(o.callback, o.scope, [o, true, r]);
11403
11404             setTimeout(function(){document.body.removeChild(frame);}, 100);
11405         }
11406
11407         Roo.EventManager.on(frame, 'load', cb, this);
11408         form.submit();
11409
11410         if(hiddens){ // remove dynamic params
11411             for(var i = 0, len = hiddens.length; i < len; i++){
11412                 form.removeChild(hiddens[i]);
11413             }
11414         }
11415     }
11416 });
11417
11418 /**
11419  * @class Roo.Ajax
11420  * @extends Roo.data.Connection
11421  * Global Ajax request class.
11422  *
11423  * @singleton
11424  */
11425 Roo.Ajax = new Roo.data.Connection({
11426     // fix up the docs
11427    /**
11428      * @cfg {String} url @hide
11429      */
11430     /**
11431      * @cfg {Object} extraParams @hide
11432      */
11433     /**
11434      * @cfg {Object} defaultHeaders @hide
11435      */
11436     /**
11437      * @cfg {String} method (Optional) @hide
11438      */
11439     /**
11440      * @cfg {Number} timeout (Optional) @hide
11441      */
11442     /**
11443      * @cfg {Boolean} autoAbort (Optional) @hide
11444      */
11445
11446     /**
11447      * @cfg {Boolean} disableCaching (Optional) @hide
11448      */
11449
11450     /**
11451      * @property  disableCaching
11452      * True to add a unique cache-buster param to GET requests. (defaults to true)
11453      * @type Boolean
11454      */
11455     /**
11456      * @property  url
11457      * The default URL to be used for requests to the server. (defaults to undefined)
11458      * @type String
11459      */
11460     /**
11461      * @property  extraParams
11462      * An object containing properties which are used as
11463      * extra parameters to each request made by this object. (defaults to undefined)
11464      * @type Object
11465      */
11466     /**
11467      * @property  defaultHeaders
11468      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11469      * @type Object
11470      */
11471     /**
11472      * @property  method
11473      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11474      * @type String
11475      */
11476     /**
11477      * @property  timeout
11478      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11479      * @type Number
11480      */
11481
11482     /**
11483      * @property  autoAbort
11484      * Whether a new request should abort any pending requests. (defaults to false)
11485      * @type Boolean
11486      */
11487     autoAbort : false,
11488
11489     /**
11490      * Serialize the passed form into a url encoded string
11491      * @param {String/HTMLElement} form
11492      * @return {String}
11493      */
11494     serializeForm : function(form){
11495         return Roo.lib.Ajax.serializeForm(form);
11496     }
11497 });/*
11498  * Based on:
11499  * Ext JS Library 1.1.1
11500  * Copyright(c) 2006-2007, Ext JS, LLC.
11501  *
11502  * Originally Released Under LGPL - original licence link has changed is not relivant.
11503  *
11504  * Fork - LGPL
11505  * <script type="text/javascript">
11506  */
11507  
11508 /**
11509  * @class Roo.Ajax
11510  * @extends Roo.data.Connection
11511  * Global Ajax request class.
11512  *
11513  * @instanceOf  Roo.data.Connection
11514  */
11515 Roo.Ajax = new Roo.data.Connection({
11516     // fix up the docs
11517     
11518     /**
11519      * fix up scoping
11520      * @scope Roo.Ajax
11521      */
11522     
11523    /**
11524      * @cfg {String} url @hide
11525      */
11526     /**
11527      * @cfg {Object} extraParams @hide
11528      */
11529     /**
11530      * @cfg {Object} defaultHeaders @hide
11531      */
11532     /**
11533      * @cfg {String} method (Optional) @hide
11534      */
11535     /**
11536      * @cfg {Number} timeout (Optional) @hide
11537      */
11538     /**
11539      * @cfg {Boolean} autoAbort (Optional) @hide
11540      */
11541
11542     /**
11543      * @cfg {Boolean} disableCaching (Optional) @hide
11544      */
11545
11546     /**
11547      * @property  disableCaching
11548      * True to add a unique cache-buster param to GET requests. (defaults to true)
11549      * @type Boolean
11550      */
11551     /**
11552      * @property  url
11553      * The default URL to be used for requests to the server. (defaults to undefined)
11554      * @type String
11555      */
11556     /**
11557      * @property  extraParams
11558      * An object containing properties which are used as
11559      * extra parameters to each request made by this object. (defaults to undefined)
11560      * @type Object
11561      */
11562     /**
11563      * @property  defaultHeaders
11564      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11565      * @type Object
11566      */
11567     /**
11568      * @property  method
11569      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11570      * @type String
11571      */
11572     /**
11573      * @property  timeout
11574      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11575      * @type Number
11576      */
11577
11578     /**
11579      * @property  autoAbort
11580      * Whether a new request should abort any pending requests. (defaults to false)
11581      * @type Boolean
11582      */
11583     autoAbort : false,
11584
11585     /**
11586      * Serialize the passed form into a url encoded string
11587      * @param {String/HTMLElement} form
11588      * @return {String}
11589      */
11590     serializeForm : function(form){
11591         return Roo.lib.Ajax.serializeForm(form);
11592     }
11593 });/*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603
11604  
11605 /**
11606  * @class Roo.UpdateManager
11607  * @extends Roo.util.Observable
11608  * Provides AJAX-style update for Element object.<br><br>
11609  * Usage:<br>
11610  * <pre><code>
11611  * // Get it from a Roo.Element object
11612  * var el = Roo.get("foo");
11613  * var mgr = el.getUpdateManager();
11614  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11615  * ...
11616  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11617  * <br>
11618  * // or directly (returns the same UpdateManager instance)
11619  * var mgr = new Roo.UpdateManager("myElementId");
11620  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11621  * mgr.on("update", myFcnNeedsToKnow);
11622  * <br>
11623    // short handed call directly from the element object
11624    Roo.get("foo").load({
11625         url: "bar.php",
11626         scripts:true,
11627         params: "for=bar",
11628         text: "Loading Foo..."
11629    });
11630  * </code></pre>
11631  * @constructor
11632  * Create new UpdateManager directly.
11633  * @param {String/HTMLElement/Roo.Element} el The element to update
11634  * @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).
11635  */
11636 Roo.UpdateManager = function(el, forceNew){
11637     el = Roo.get(el);
11638     if(!forceNew && el.updateManager){
11639         return el.updateManager;
11640     }
11641     /**
11642      * The Element object
11643      * @type Roo.Element
11644      */
11645     this.el = el;
11646     /**
11647      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11648      * @type String
11649      */
11650     this.defaultUrl = null;
11651
11652     this.addEvents({
11653         /**
11654          * @event beforeupdate
11655          * Fired before an update is made, return false from your handler and the update is cancelled.
11656          * @param {Roo.Element} el
11657          * @param {String/Object/Function} url
11658          * @param {String/Object} params
11659          */
11660         "beforeupdate": true,
11661         /**
11662          * @event update
11663          * Fired after successful update is made.
11664          * @param {Roo.Element} el
11665          * @param {Object} oResponseObject The response Object
11666          */
11667         "update": true,
11668         /**
11669          * @event failure
11670          * Fired on update failure.
11671          * @param {Roo.Element} el
11672          * @param {Object} oResponseObject The response Object
11673          */
11674         "failure": true
11675     });
11676     var d = Roo.UpdateManager.defaults;
11677     /**
11678      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11679      * @type String
11680      */
11681     this.sslBlankUrl = d.sslBlankUrl;
11682     /**
11683      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11684      * @type Boolean
11685      */
11686     this.disableCaching = d.disableCaching;
11687     /**
11688      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11689      * @type String
11690      */
11691     this.indicatorText = d.indicatorText;
11692     /**
11693      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11694      * @type String
11695      */
11696     this.showLoadIndicator = d.showLoadIndicator;
11697     /**
11698      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11699      * @type Number
11700      */
11701     this.timeout = d.timeout;
11702
11703     /**
11704      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11705      * @type Boolean
11706      */
11707     this.loadScripts = d.loadScripts;
11708
11709     /**
11710      * Transaction object of current executing transaction
11711      */
11712     this.transaction = null;
11713
11714     /**
11715      * @private
11716      */
11717     this.autoRefreshProcId = null;
11718     /**
11719      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11720      * @type Function
11721      */
11722     this.refreshDelegate = this.refresh.createDelegate(this);
11723     /**
11724      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11725      * @type Function
11726      */
11727     this.updateDelegate = this.update.createDelegate(this);
11728     /**
11729      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11730      * @type Function
11731      */
11732     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11733     /**
11734      * @private
11735      */
11736     this.successDelegate = this.processSuccess.createDelegate(this);
11737     /**
11738      * @private
11739      */
11740     this.failureDelegate = this.processFailure.createDelegate(this);
11741
11742     if(!this.renderer){
11743      /**
11744       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11745       */
11746     this.renderer = new Roo.UpdateManager.BasicRenderer();
11747     }
11748     
11749     Roo.UpdateManager.superclass.constructor.call(this);
11750 };
11751
11752 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11753     /**
11754      * Get the Element this UpdateManager is bound to
11755      * @return {Roo.Element} The element
11756      */
11757     getEl : function(){
11758         return this.el;
11759     },
11760     /**
11761      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11762      * @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:
11763 <pre><code>
11764 um.update({<br/>
11765     url: "your-url.php",<br/>
11766     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11767     callback: yourFunction,<br/>
11768     scope: yourObject, //(optional scope)  <br/>
11769     discardUrl: false, <br/>
11770     nocache: false,<br/>
11771     text: "Loading...",<br/>
11772     timeout: 30,<br/>
11773     scripts: false<br/>
11774 });
11775 </code></pre>
11776      * The only required property is url. The optional properties nocache, text and scripts
11777      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11778      * @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}
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      * @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.
11781      */
11782     update : function(url, params, callback, discardUrl){
11783         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11784             var method = this.method, cfg;
11785             if(typeof url == "object"){ // must be config object
11786                 cfg = url;
11787                 url = cfg.url;
11788                 params = params || cfg.params;
11789                 callback = callback || cfg.callback;
11790                 discardUrl = discardUrl || cfg.discardUrl;
11791                 if(callback && cfg.scope){
11792                     callback = callback.createDelegate(cfg.scope);
11793                 }
11794                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11795                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11796                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11797                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11798                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11799             }
11800             this.showLoading();
11801             if(!discardUrl){
11802                 this.defaultUrl = url;
11803             }
11804             if(typeof url == "function"){
11805                 url = url.call(this);
11806             }
11807
11808             method = method || (params ? "POST" : "GET");
11809             if(method == "GET"){
11810                 url = this.prepareUrl(url);
11811             }
11812
11813             var o = Roo.apply(cfg ||{}, {
11814                 url : url,
11815                 params: params,
11816                 success: this.successDelegate,
11817                 failure: this.failureDelegate,
11818                 callback: undefined,
11819                 timeout: (this.timeout*1000),
11820                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11821             });
11822
11823             this.transaction = Roo.Ajax.request(o);
11824         }
11825     },
11826
11827     /**
11828      * 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.
11829      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11830      * @param {String/HTMLElement} form The form Id or form element
11831      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11832      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11833      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11834      */
11835     formUpdate : function(form, url, reset, callback){
11836         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11837             if(typeof url == "function"){
11838                 url = url.call(this);
11839             }
11840             form = Roo.getDom(form);
11841             this.transaction = Roo.Ajax.request({
11842                 form: form,
11843                 url:url,
11844                 success: this.successDelegate,
11845                 failure: this.failureDelegate,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11848             });
11849             this.showLoading.defer(1, this);
11850         }
11851     },
11852
11853     /**
11854      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11856      */
11857     refresh : function(callback){
11858         if(this.defaultUrl == null){
11859             return;
11860         }
11861         this.update(this.defaultUrl, null, callback, true);
11862     },
11863
11864     /**
11865      * Set this element to auto refresh.
11866      * @param {Number} interval How often to update (in seconds).
11867      * @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)
11868      * @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}
11869      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11870      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11871      */
11872     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11873         if(refreshNow){
11874             this.update(url || this.defaultUrl, params, callback, true);
11875         }
11876         if(this.autoRefreshProcId){
11877             clearInterval(this.autoRefreshProcId);
11878         }
11879         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11880     },
11881
11882     /**
11883      * Stop auto refresh on this element.
11884      */
11885      stopAutoRefresh : function(){
11886         if(this.autoRefreshProcId){
11887             clearInterval(this.autoRefreshProcId);
11888             delete this.autoRefreshProcId;
11889         }
11890     },
11891
11892     isAutoRefreshing : function(){
11893        return this.autoRefreshProcId ? true : false;
11894     },
11895     /**
11896      * Called to update the element to "Loading" state. Override to perform custom action.
11897      */
11898     showLoading : function(){
11899         if(this.showLoadIndicator){
11900             this.el.update(this.indicatorText);
11901         }
11902     },
11903
11904     /**
11905      * Adds unique parameter to query string if disableCaching = true
11906      * @private
11907      */
11908     prepareUrl : function(url){
11909         if(this.disableCaching){
11910             var append = "_dc=" + (new Date().getTime());
11911             if(url.indexOf("?") !== -1){
11912                 url += "&" + append;
11913             }else{
11914                 url += "?" + append;
11915             }
11916         }
11917         return url;
11918     },
11919
11920     /**
11921      * @private
11922      */
11923     processSuccess : function(response){
11924         this.transaction = null;
11925         if(response.argument.form && response.argument.reset){
11926             try{ // put in try/catch since some older FF releases had problems with this
11927                 response.argument.form.reset();
11928             }catch(e){}
11929         }
11930         if(this.loadScripts){
11931             this.renderer.render(this.el, response, this,
11932                 this.updateComplete.createDelegate(this, [response]));
11933         }else{
11934             this.renderer.render(this.el, response, this);
11935             this.updateComplete(response);
11936         }
11937     },
11938
11939     updateComplete : function(response){
11940         this.fireEvent("update", this.el, response);
11941         if(typeof response.argument.callback == "function"){
11942             response.argument.callback(this.el, true, response);
11943         }
11944     },
11945
11946     /**
11947      * @private
11948      */
11949     processFailure : function(response){
11950         this.transaction = null;
11951         this.fireEvent("failure", this.el, response);
11952         if(typeof response.argument.callback == "function"){
11953             response.argument.callback(this.el, false, response);
11954         }
11955     },
11956
11957     /**
11958      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11959      * @param {Object} renderer The object implementing the render() method
11960      */
11961     setRenderer : function(renderer){
11962         this.renderer = renderer;
11963     },
11964
11965     getRenderer : function(){
11966        return this.renderer;
11967     },
11968
11969     /**
11970      * Set the defaultUrl used for updates
11971      * @param {String/Function} defaultUrl The url or a function to call to get the url
11972      */
11973     setDefaultUrl : function(defaultUrl){
11974         this.defaultUrl = defaultUrl;
11975     },
11976
11977     /**
11978      * Aborts the executing transaction
11979      */
11980     abort : function(){
11981         if(this.transaction){
11982             Roo.Ajax.abort(this.transaction);
11983         }
11984     },
11985
11986     /**
11987      * Returns true if an update is in progress
11988      * @return {Boolean}
11989      */
11990     isUpdating : function(){
11991         if(this.transaction){
11992             return Roo.Ajax.isLoading(this.transaction);
11993         }
11994         return false;
11995     }
11996 });
11997
11998 /**
11999  * @class Roo.UpdateManager.defaults
12000  * @static (not really - but it helps the doc tool)
12001  * The defaults collection enables customizing the default properties of UpdateManager
12002  */
12003    Roo.UpdateManager.defaults = {
12004        /**
12005          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12006          * @type Number
12007          */
12008          timeout : 30,
12009
12010          /**
12011          * True to process scripts by default (Defaults to false).
12012          * @type Boolean
12013          */
12014         loadScripts : false,
12015
12016         /**
12017         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12018         * @type String
12019         */
12020         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12021         /**
12022          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12023          * @type Boolean
12024          */
12025         disableCaching : false,
12026         /**
12027          * Whether to show indicatorText when loading (Defaults to true).
12028          * @type Boolean
12029          */
12030         showLoadIndicator : true,
12031         /**
12032          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12033          * @type String
12034          */
12035         indicatorText : '<div class="loading-indicator">Loading...</div>'
12036    };
12037
12038 /**
12039  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12040  *Usage:
12041  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12042  * @param {String/HTMLElement/Roo.Element} el The element to update
12043  * @param {String} url The url
12044  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12045  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12046  * @static
12047  * @deprecated
12048  * @member Roo.UpdateManager
12049  */
12050 Roo.UpdateManager.updateElement = function(el, url, params, options){
12051     var um = Roo.get(el, true).getUpdateManager();
12052     Roo.apply(um, options);
12053     um.update(url, params, options ? options.callback : null);
12054 };
12055 // alias for backwards compat
12056 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12057 /**
12058  * @class Roo.UpdateManager.BasicRenderer
12059  * Default Content renderer. Updates the elements innerHTML with the responseText.
12060  */
12061 Roo.UpdateManager.BasicRenderer = function(){};
12062
12063 Roo.UpdateManager.BasicRenderer.prototype = {
12064     /**
12065      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12066      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12067      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12068      * @param {Roo.Element} el The element being rendered
12069      * @param {Object} response The YUI Connect response object
12070      * @param {UpdateManager} updateManager The calling update manager
12071      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12072      */
12073      render : function(el, response, updateManager, callback){
12074         el.update(response.responseText, updateManager.loadScripts, callback);
12075     }
12076 };
12077 /*
12078  * Based on:
12079  * Ext JS Library 1.1.1
12080  * Copyright(c) 2006-2007, Ext JS, LLC.
12081  *
12082  * Originally Released Under LGPL - original licence link has changed is not relivant.
12083  *
12084  * Fork - LGPL
12085  * <script type="text/javascript">
12086  */
12087
12088 /**
12089  * @class Roo.util.DelayedTask
12090  * Provides a convenient method of performing setTimeout where a new
12091  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12092  * You can use this class to buffer
12093  * the keypress events for a certain number of milliseconds, and perform only if they stop
12094  * for that amount of time.
12095  * @constructor The parameters to this constructor serve as defaults and are not required.
12096  * @param {Function} fn (optional) The default function to timeout
12097  * @param {Object} scope (optional) The default scope of that timeout
12098  * @param {Array} args (optional) The default Array of arguments
12099  */
12100 Roo.util.DelayedTask = function(fn, scope, args){
12101     var id = null, d, t;
12102
12103     var call = function(){
12104         var now = new Date().getTime();
12105         if(now - t >= d){
12106             clearInterval(id);
12107             id = null;
12108             fn.apply(scope, args || []);
12109         }
12110     };
12111     /**
12112      * Cancels any pending timeout and queues a new one
12113      * @param {Number} delay The milliseconds to delay
12114      * @param {Function} newFn (optional) Overrides function passed to constructor
12115      * @param {Object} newScope (optional) Overrides scope passed to constructor
12116      * @param {Array} newArgs (optional) Overrides args passed to constructor
12117      */
12118     this.delay = function(delay, newFn, newScope, newArgs){
12119         if(id && delay != d){
12120             this.cancel();
12121         }
12122         d = delay;
12123         t = new Date().getTime();
12124         fn = newFn || fn;
12125         scope = newScope || scope;
12126         args = newArgs || args;
12127         if(!id){
12128             id = setInterval(call, d);
12129         }
12130     };
12131
12132     /**
12133      * Cancel the last queued timeout
12134      */
12135     this.cancel = function(){
12136         if(id){
12137             clearInterval(id);
12138             id = null;
12139         }
12140     };
12141 };/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151  
12152  
12153 Roo.util.TaskRunner = function(interval){
12154     interval = interval || 10;
12155     var tasks = [], removeQueue = [];
12156     var id = 0;
12157     var running = false;
12158
12159     var stopThread = function(){
12160         running = false;
12161         clearInterval(id);
12162         id = 0;
12163     };
12164
12165     var startThread = function(){
12166         if(!running){
12167             running = true;
12168             id = setInterval(runTasks, interval);
12169         }
12170     };
12171
12172     var removeTask = function(task){
12173         removeQueue.push(task);
12174         if(task.onStop){
12175             task.onStop();
12176         }
12177     };
12178
12179     var runTasks = function(){
12180         if(removeQueue.length > 0){
12181             for(var i = 0, len = removeQueue.length; i < len; i++){
12182                 tasks.remove(removeQueue[i]);
12183             }
12184             removeQueue = [];
12185             if(tasks.length < 1){
12186                 stopThread();
12187                 return;
12188             }
12189         }
12190         var now = new Date().getTime();
12191         for(var i = 0, len = tasks.length; i < len; ++i){
12192             var t = tasks[i];
12193             var itime = now - t.taskRunTime;
12194             if(t.interval <= itime){
12195                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12196                 t.taskRunTime = now;
12197                 if(rt === false || t.taskRunCount === t.repeat){
12198                     removeTask(t);
12199                     return;
12200                 }
12201             }
12202             if(t.duration && t.duration <= (now - t.taskStartTime)){
12203                 removeTask(t);
12204             }
12205         }
12206     };
12207
12208     /**
12209      * Queues a new task.
12210      * @param {Object} task
12211      */
12212     this.start = function(task){
12213         tasks.push(task);
12214         task.taskStartTime = new Date().getTime();
12215         task.taskRunTime = 0;
12216         task.taskRunCount = 0;
12217         startThread();
12218         return task;
12219     };
12220
12221     this.stop = function(task){
12222         removeTask(task);
12223         return task;
12224     };
12225
12226     this.stopAll = function(){
12227         stopThread();
12228         for(var i = 0, len = tasks.length; i < len; i++){
12229             if(tasks[i].onStop){
12230                 tasks[i].onStop();
12231             }
12232         }
12233         tasks = [];
12234         removeQueue = [];
12235     };
12236 };
12237
12238 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12239  * Based on:
12240  * Ext JS Library 1.1.1
12241  * Copyright(c) 2006-2007, Ext JS, LLC.
12242  *
12243  * Originally Released Under LGPL - original licence link has changed is not relivant.
12244  *
12245  * Fork - LGPL
12246  * <script type="text/javascript">
12247  */
12248
12249  
12250 /**
12251  * @class Roo.util.MixedCollection
12252  * @extends Roo.util.Observable
12253  * A Collection class that maintains both numeric indexes and keys and exposes events.
12254  * @constructor
12255  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12256  * collection (defaults to false)
12257  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12258  * and return the key value for that item.  This is used when available to look up the key on items that
12259  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12260  * equivalent to providing an implementation for the {@link #getKey} method.
12261  */
12262 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12263     this.items = [];
12264     this.map = {};
12265     this.keys = [];
12266     this.length = 0;
12267     this.addEvents({
12268         /**
12269          * @event clear
12270          * Fires when the collection is cleared.
12271          */
12272         "clear" : true,
12273         /**
12274          * @event add
12275          * Fires when an item is added to the collection.
12276          * @param {Number} index The index at which the item was added.
12277          * @param {Object} o The item added.
12278          * @param {String} key The key associated with the added item.
12279          */
12280         "add" : true,
12281         /**
12282          * @event replace
12283          * Fires when an item is replaced in the collection.
12284          * @param {String} key he key associated with the new added.
12285          * @param {Object} old The item being replaced.
12286          * @param {Object} new The new item.
12287          */
12288         "replace" : true,
12289         /**
12290          * @event remove
12291          * Fires when an item is removed from the collection.
12292          * @param {Object} o The item being removed.
12293          * @param {String} key (optional) The key associated with the removed item.
12294          */
12295         "remove" : true,
12296         "sort" : true
12297     });
12298     this.allowFunctions = allowFunctions === true;
12299     if(keyFn){
12300         this.getKey = keyFn;
12301     }
12302     Roo.util.MixedCollection.superclass.constructor.call(this);
12303 };
12304
12305 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12306     allowFunctions : false,
12307     
12308 /**
12309  * Adds an item to the collection.
12310  * @param {String} key The key to associate with the item
12311  * @param {Object} o The item to add.
12312  * @return {Object} The item added.
12313  */
12314     add : function(key, o){
12315         if(arguments.length == 1){
12316             o = arguments[0];
12317             key = this.getKey(o);
12318         }
12319         if(typeof key == "undefined" || key === null){
12320             this.length++;
12321             this.items.push(o);
12322             this.keys.push(null);
12323         }else{
12324             var old = this.map[key];
12325             if(old){
12326                 return this.replace(key, o);
12327             }
12328             this.length++;
12329             this.items.push(o);
12330             this.map[key] = o;
12331             this.keys.push(key);
12332         }
12333         this.fireEvent("add", this.length-1, o, key);
12334         return o;
12335     },
12336        
12337 /**
12338   * MixedCollection has a generic way to fetch keys if you implement getKey.
12339 <pre><code>
12340 // normal way
12341 var mc = new Roo.util.MixedCollection();
12342 mc.add(someEl.dom.id, someEl);
12343 mc.add(otherEl.dom.id, otherEl);
12344 //and so on
12345
12346 // using getKey
12347 var mc = new Roo.util.MixedCollection();
12348 mc.getKey = function(el){
12349    return el.dom.id;
12350 };
12351 mc.add(someEl);
12352 mc.add(otherEl);
12353
12354 // or via the constructor
12355 var mc = new Roo.util.MixedCollection(false, function(el){
12356    return el.dom.id;
12357 });
12358 mc.add(someEl);
12359 mc.add(otherEl);
12360 </code></pre>
12361  * @param o {Object} The item for which to find the key.
12362  * @return {Object} The key for the passed item.
12363  */
12364     getKey : function(o){
12365          return o.id; 
12366     },
12367    
12368 /**
12369  * Replaces an item in the collection.
12370  * @param {String} key The key associated with the item to replace, or the item to replace.
12371  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12372  * @return {Object}  The new item.
12373  */
12374     replace : function(key, o){
12375         if(arguments.length == 1){
12376             o = arguments[0];
12377             key = this.getKey(o);
12378         }
12379         var old = this.item(key);
12380         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12381              return this.add(key, o);
12382         }
12383         var index = this.indexOfKey(key);
12384         this.items[index] = o;
12385         this.map[key] = o;
12386         this.fireEvent("replace", key, old, o);
12387         return o;
12388     },
12389    
12390 /**
12391  * Adds all elements of an Array or an Object to the collection.
12392  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12393  * an Array of values, each of which are added to the collection.
12394  */
12395     addAll : function(objs){
12396         if(arguments.length > 1 || objs instanceof Array){
12397             var args = arguments.length > 1 ? arguments : objs;
12398             for(var i = 0, len = args.length; i < len; i++){
12399                 this.add(args[i]);
12400             }
12401         }else{
12402             for(var key in objs){
12403                 if(this.allowFunctions || typeof objs[key] != "function"){
12404                     this.add(key, objs[key]);
12405                 }
12406             }
12407         }
12408     },
12409    
12410 /**
12411  * Executes the specified function once for every item in the collection, passing each
12412  * item as the first and only parameter. returning false from the function will stop the iteration.
12413  * @param {Function} fn The function to execute for each item.
12414  * @param {Object} scope (optional) The scope in which to execute the function.
12415  */
12416     each : function(fn, scope){
12417         var items = [].concat(this.items); // each safe for removal
12418         for(var i = 0, len = items.length; i < len; i++){
12419             if(fn.call(scope || items[i], items[i], i, len) === false){
12420                 break;
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every key in the collection, passing each
12427  * key, and its associated item as the first two parameters.
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     eachKey : function(fn, scope){
12432         for(var i = 0, len = this.keys.length; i < len; i++){
12433             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12434         }
12435     },
12436    
12437 /**
12438  * Returns the first item in the collection which elicits a true return value from the
12439  * passed selection function.
12440  * @param {Function} fn The selection function to execute for each item.
12441  * @param {Object} scope (optional) The scope in which to execute the function.
12442  * @return {Object} The first item in the collection which returned true from the selection function.
12443  */
12444     find : function(fn, scope){
12445         for(var i = 0, len = this.items.length; i < len; i++){
12446             if(fn.call(scope || window, this.items[i], this.keys[i])){
12447                 return this.items[i];
12448             }
12449         }
12450         return null;
12451     },
12452    
12453 /**
12454  * Inserts an item at the specified index in the collection.
12455  * @param {Number} index The index to insert the item at.
12456  * @param {String} key The key to associate with the new item, or the item itself.
12457  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12458  * @return {Object} The item inserted.
12459  */
12460     insert : function(index, key, o){
12461         if(arguments.length == 2){
12462             o = arguments[1];
12463             key = this.getKey(o);
12464         }
12465         if(index >= this.length){
12466             return this.add(key, o);
12467         }
12468         this.length++;
12469         this.items.splice(index, 0, o);
12470         if(typeof key != "undefined" && key != null){
12471             this.map[key] = o;
12472         }
12473         this.keys.splice(index, 0, key);
12474         this.fireEvent("add", index, o, key);
12475         return o;
12476     },
12477    
12478 /**
12479  * Removed an item from the collection.
12480  * @param {Object} o The item to remove.
12481  * @return {Object} The item removed.
12482  */
12483     remove : function(o){
12484         return this.removeAt(this.indexOf(o));
12485     },
12486    
12487 /**
12488  * Remove an item from a specified index in the collection.
12489  * @param {Number} index The index within the collection of the item to remove.
12490  */
12491     removeAt : function(index){
12492         if(index < this.length && index >= 0){
12493             this.length--;
12494             var o = this.items[index];
12495             this.items.splice(index, 1);
12496             var key = this.keys[index];
12497             if(typeof key != "undefined"){
12498                 delete this.map[key];
12499             }
12500             this.keys.splice(index, 1);
12501             this.fireEvent("remove", o, key);
12502         }
12503     },
12504    
12505 /**
12506  * Removed an item associated with the passed key fom the collection.
12507  * @param {String} key The key of the item to remove.
12508  */
12509     removeKey : function(key){
12510         return this.removeAt(this.indexOfKey(key));
12511     },
12512    
12513 /**
12514  * Returns the number of items in the collection.
12515  * @return {Number} the number of items in the collection.
12516  */
12517     getCount : function(){
12518         return this.length; 
12519     },
12520    
12521 /**
12522  * Returns index within the collection of the passed Object.
12523  * @param {Object} o The item to find the index of.
12524  * @return {Number} index of the item.
12525  */
12526     indexOf : function(o){
12527         if(!this.items.indexOf){
12528             for(var i = 0, len = this.items.length; i < len; i++){
12529                 if(this.items[i] == o) return i;
12530             }
12531             return -1;
12532         }else{
12533             return this.items.indexOf(o);
12534         }
12535     },
12536    
12537 /**
12538  * Returns index within the collection of the passed key.
12539  * @param {String} key The key to find the index of.
12540  * @return {Number} index of the key.
12541  */
12542     indexOfKey : function(key){
12543         if(!this.keys.indexOf){
12544             for(var i = 0, len = this.keys.length; i < len; i++){
12545                 if(this.keys[i] == key) return i;
12546             }
12547             return -1;
12548         }else{
12549             return this.keys.indexOf(key);
12550         }
12551     },
12552    
12553 /**
12554  * Returns the item associated with the passed key OR index. Key has priority over index.
12555  * @param {String/Number} key The key or index of the item.
12556  * @return {Object} The item associated with the passed key.
12557  */
12558     item : function(key){
12559         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12560         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12561     },
12562     
12563 /**
12564  * Returns the item at the specified index.
12565  * @param {Number} index The index of the item.
12566  * @return {Object}
12567  */
12568     itemAt : function(index){
12569         return this.items[index];
12570     },
12571     
12572 /**
12573  * Returns the item associated with the passed key.
12574  * @param {String/Number} key The key of the item.
12575  * @return {Object} The item associated with the passed key.
12576  */
12577     key : function(key){
12578         return this.map[key];
12579     },
12580    
12581 /**
12582  * Returns true if the collection contains the passed Object as an item.
12583  * @param {Object} o  The Object to look for in the collection.
12584  * @return {Boolean} True if the collection contains the Object as an item.
12585  */
12586     contains : function(o){
12587         return this.indexOf(o) != -1;
12588     },
12589    
12590 /**
12591  * Returns true if the collection contains the passed Object as a key.
12592  * @param {String} key The key to look for in the collection.
12593  * @return {Boolean} True if the collection contains the Object as a key.
12594  */
12595     containsKey : function(key){
12596         return typeof this.map[key] != "undefined";
12597     },
12598    
12599 /**
12600  * Removes all items from the collection.
12601  */
12602     clear : function(){
12603         this.length = 0;
12604         this.items = [];
12605         this.keys = [];
12606         this.map = {};
12607         this.fireEvent("clear");
12608     },
12609    
12610 /**
12611  * Returns the first item in the collection.
12612  * @return {Object} the first item in the collection..
12613  */
12614     first : function(){
12615         return this.items[0]; 
12616     },
12617    
12618 /**
12619  * Returns the last item in the collection.
12620  * @return {Object} the last item in the collection..
12621  */
12622     last : function(){
12623         return this.items[this.length-1];   
12624     },
12625     
12626     _sort : function(property, dir, fn){
12627         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12628         fn = fn || function(a, b){
12629             return a-b;
12630         };
12631         var c = [], k = this.keys, items = this.items;
12632         for(var i = 0, len = items.length; i < len; i++){
12633             c[c.length] = {key: k[i], value: items[i], index: i};
12634         }
12635         c.sort(function(a, b){
12636             var v = fn(a[property], b[property]) * dsc;
12637             if(v == 0){
12638                 v = (a.index < b.index ? -1 : 1);
12639             }
12640             return v;
12641         });
12642         for(var i = 0, len = c.length; i < len; i++){
12643             items[i] = c[i].value;
12644             k[i] = c[i].key;
12645         }
12646         this.fireEvent("sort", this);
12647     },
12648     
12649     /**
12650      * Sorts this collection with the passed comparison function
12651      * @param {String} direction (optional) "ASC" or "DESC"
12652      * @param {Function} fn (optional) comparison function
12653      */
12654     sort : function(dir, fn){
12655         this._sort("value", dir, fn);
12656     },
12657     
12658     /**
12659      * Sorts this collection by keys
12660      * @param {String} direction (optional) "ASC" or "DESC"
12661      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12662      */
12663     keySort : function(dir, fn){
12664         this._sort("key", dir, fn || function(a, b){
12665             return String(a).toUpperCase()-String(b).toUpperCase();
12666         });
12667     },
12668     
12669     /**
12670      * Returns a range of items in this collection
12671      * @param {Number} startIndex (optional) defaults to 0
12672      * @param {Number} endIndex (optional) default to the last item
12673      * @return {Array} An array of items
12674      */
12675     getRange : function(start, end){
12676         var items = this.items;
12677         if(items.length < 1){
12678             return [];
12679         }
12680         start = start || 0;
12681         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12682         var r = [];
12683         if(start <= end){
12684             for(var i = start; i <= end; i++) {
12685                     r[r.length] = items[i];
12686             }
12687         }else{
12688             for(var i = start; i >= end; i--) {
12689                     r[r.length] = items[i];
12690             }
12691         }
12692         return r;
12693     },
12694         
12695     /**
12696      * Filter the <i>objects</i> in this collection by a specific property. 
12697      * Returns a new collection that has been filtered.
12698      * @param {String} property A property on your objects
12699      * @param {String/RegExp} value Either string that the property values 
12700      * should start with or a RegExp to test against the property
12701      * @return {MixedCollection} The new filtered collection
12702      */
12703     filter : function(property, value){
12704         if(!value.exec){ // not a regex
12705             value = String(value);
12706             if(value.length == 0){
12707                 return this.clone();
12708             }
12709             value = new RegExp("^" + Roo.escapeRe(value), "i");
12710         }
12711         return this.filterBy(function(o){
12712             return o && value.test(o[property]);
12713         });
12714         },
12715     
12716     /**
12717      * Filter by a function. * Returns a new collection that has been filtered.
12718      * The passed function will be called with each 
12719      * object in the collection. If the function returns true, the value is included 
12720      * otherwise it is filtered.
12721      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12722      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12723      * @return {MixedCollection} The new filtered collection
12724      */
12725     filterBy : function(fn, scope){
12726         var r = new Roo.util.MixedCollection();
12727         r.getKey = this.getKey;
12728         var k = this.keys, it = this.items;
12729         for(var i = 0, len = it.length; i < len; i++){
12730             if(fn.call(scope||this, it[i], k[i])){
12731                                 r.add(k[i], it[i]);
12732                         }
12733         }
12734         return r;
12735     },
12736     
12737     /**
12738      * Creates a duplicate of this collection
12739      * @return {MixedCollection}
12740      */
12741     clone : function(){
12742         var r = new Roo.util.MixedCollection();
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             r.add(k[i], it[i]);
12746         }
12747         r.getKey = this.getKey;
12748         return r;
12749     }
12750 });
12751 /**
12752  * Returns the item associated with the passed key or index.
12753  * @method
12754  * @param {String/Number} key The key or index of the item.
12755  * @return {Object} The item associated with the passed key.
12756  */
12757 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12758  * Based on:
12759  * Ext JS Library 1.1.1
12760  * Copyright(c) 2006-2007, Ext JS, LLC.
12761  *
12762  * Originally Released Under LGPL - original licence link has changed is not relivant.
12763  *
12764  * Fork - LGPL
12765  * <script type="text/javascript">
12766  */
12767 /**
12768  * @class Roo.util.JSON
12769  * Modified version of Douglas Crockford"s json.js that doesn"t
12770  * mess with the Object prototype 
12771  * http://www.json.org/js.html
12772  * @singleton
12773  */
12774 Roo.util.JSON = new (function(){
12775     var useHasOwn = {}.hasOwnProperty ? true : false;
12776     
12777     // crashes Safari in some instances
12778     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12779     
12780     var pad = function(n) {
12781         return n < 10 ? "0" + n : n;
12782     };
12783     
12784     var m = {
12785         "\b": '\\b',
12786         "\t": '\\t',
12787         "\n": '\\n',
12788         "\f": '\\f',
12789         "\r": '\\r',
12790         '"' : '\\"',
12791         "\\": '\\\\'
12792     };
12793
12794     var encodeString = function(s){
12795         if (/["\\\x00-\x1f]/.test(s)) {
12796             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12797                 var c = m[b];
12798                 if(c){
12799                     return c;
12800                 }
12801                 c = b.charCodeAt();
12802                 return "\\u00" +
12803                     Math.floor(c / 16).toString(16) +
12804                     (c % 16).toString(16);
12805             }) + '"';
12806         }
12807         return '"' + s + '"';
12808     };
12809     
12810     var encodeArray = function(o){
12811         var a = ["["], b, i, l = o.length, v;
12812             for (i = 0; i < l; i += 1) {
12813                 v = o[i];
12814                 switch (typeof v) {
12815                     case "undefined":
12816                     case "function":
12817                     case "unknown":
12818                         break;
12819                     default:
12820                         if (b) {
12821                             a.push(',');
12822                         }
12823                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12824                         b = true;
12825                 }
12826             }
12827             a.push("]");
12828             return a.join("");
12829     };
12830     
12831     var encodeDate = function(o){
12832         return '"' + o.getFullYear() + "-" +
12833                 pad(o.getMonth() + 1) + "-" +
12834                 pad(o.getDate()) + "T" +
12835                 pad(o.getHours()) + ":" +
12836                 pad(o.getMinutes()) + ":" +
12837                 pad(o.getSeconds()) + '"';
12838     };
12839     
12840     /**
12841      * Encodes an Object, Array or other value
12842      * @param {Mixed} o The variable to encode
12843      * @return {String} The JSON string
12844      */
12845     this.encode = function(o)
12846     {
12847         // should this be extended to fully wrap stringify..
12848         
12849         if(typeof o == "undefined" || o === null){
12850             return "null";
12851         }else if(o instanceof Array){
12852             return encodeArray(o);
12853         }else if(o instanceof Date){
12854             return encodeDate(o);
12855         }else if(typeof o == "string"){
12856             return encodeString(o);
12857         }else if(typeof o == "number"){
12858             return isFinite(o) ? String(o) : "null";
12859         }else if(typeof o == "boolean"){
12860             return String(o);
12861         }else {
12862             var a = ["{"], b, i, v;
12863             for (i in o) {
12864                 if(!useHasOwn || o.hasOwnProperty(i)) {
12865                     v = o[i];
12866                     switch (typeof v) {
12867                     case "undefined":
12868                     case "function":
12869                     case "unknown":
12870                         break;
12871                     default:
12872                         if(b){
12873                             a.push(',');
12874                         }
12875                         a.push(this.encode(i), ":",
12876                                 v === null ? "null" : this.encode(v));
12877                         b = true;
12878                     }
12879                 }
12880             }
12881             a.push("}");
12882             return a.join("");
12883         }
12884     };
12885     
12886     /**
12887      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12888      * @param {String} json The JSON string
12889      * @return {Object} The resulting object
12890      */
12891     this.decode = function(json){
12892         
12893         return  /** eval:var:json */ eval("(" + json + ')');
12894     };
12895 })();
12896 /** 
12897  * Shorthand for {@link Roo.util.JSON#encode}
12898  * @member Roo encode 
12899  * @method */
12900 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12901 /** 
12902  * Shorthand for {@link Roo.util.JSON#decode}
12903  * @member Roo decode 
12904  * @method */
12905 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12906 /*
12907  * Based on:
12908  * Ext JS Library 1.1.1
12909  * Copyright(c) 2006-2007, Ext JS, LLC.
12910  *
12911  * Originally Released Under LGPL - original licence link has changed is not relivant.
12912  *
12913  * Fork - LGPL
12914  * <script type="text/javascript">
12915  */
12916  
12917 /**
12918  * @class Roo.util.Format
12919  * Reusable data formatting functions
12920  * @singleton
12921  */
12922 Roo.util.Format = function(){
12923     var trimRe = /^\s+|\s+$/g;
12924     return {
12925         /**
12926          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12927          * @param {String} value The string to truncate
12928          * @param {Number} length The maximum length to allow before truncating
12929          * @return {String} The converted text
12930          */
12931         ellipsis : function(value, len){
12932             if(value && value.length > len){
12933                 return value.substr(0, len-3)+"...";
12934             }
12935             return value;
12936         },
12937
12938         /**
12939          * Checks a reference and converts it to empty string if it is undefined
12940          * @param {Mixed} value Reference to check
12941          * @return {Mixed} Empty string if converted, otherwise the original value
12942          */
12943         undef : function(value){
12944             return typeof value != "undefined" ? value : "";
12945         },
12946
12947         /**
12948          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12949          * @param {String} value The string to encode
12950          * @return {String} The encoded text
12951          */
12952         htmlEncode : function(value){
12953             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12954         },
12955
12956         /**
12957          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12958          * @param {String} value The string to decode
12959          * @return {String} The decoded text
12960          */
12961         htmlDecode : function(value){
12962             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12963         },
12964
12965         /**
12966          * Trims any whitespace from either side of a string
12967          * @param {String} value The text to trim
12968          * @return {String} The trimmed text
12969          */
12970         trim : function(value){
12971             return String(value).replace(trimRe, "");
12972         },
12973
12974         /**
12975          * Returns a substring from within an original string
12976          * @param {String} value The original text
12977          * @param {Number} start The start index of the substring
12978          * @param {Number} length The length of the substring
12979          * @return {String} The substring
12980          */
12981         substr : function(value, start, length){
12982             return String(value).substr(start, length);
12983         },
12984
12985         /**
12986          * Converts a string to all lower case letters
12987          * @param {String} value The text to convert
12988          * @return {String} The converted text
12989          */
12990         lowercase : function(value){
12991             return String(value).toLowerCase();
12992         },
12993
12994         /**
12995          * Converts a string to all upper case letters
12996          * @param {String} value The text to convert
12997          * @return {String} The converted text
12998          */
12999         uppercase : function(value){
13000             return String(value).toUpperCase();
13001         },
13002
13003         /**
13004          * Converts the first character only of a string to upper case
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         capitalize : function(value){
13009             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13010         },
13011
13012         // private
13013         call : function(value, fn){
13014             if(arguments.length > 2){
13015                 var args = Array.prototype.slice.call(arguments, 2);
13016                 args.unshift(value);
13017                  
13018                 return /** eval:var:value */  eval(fn).apply(window, args);
13019             }else{
13020                 /** eval:var:value */
13021                 return /** eval:var:value */ eval(fn).call(window, value);
13022             }
13023         },
13024
13025        
13026         /**
13027          * safer version of Math.toFixed..??/
13028          * @param {Number/String} value The numeric value to format
13029          * @param {Number/String} value Decimal places 
13030          * @return {String} The formatted currency string
13031          */
13032         toFixed : function(v, n)
13033         {
13034             // why not use to fixed - precision is buggered???
13035             if (!n) {
13036                 return Math.round(v-0);
13037             }
13038             var fact = Math.pow(10,n+1);
13039             v = (Math.round((v-0)*fact))/fact;
13040             var z = (''+fact).substring(2);
13041             if (v == Math.floor(v)) {
13042                 return Math.floor(v) + '.' + z;
13043             }
13044             
13045             // now just padd decimals..
13046             var ps = String(v).split('.');
13047             var fd = (ps[1] + z);
13048             var r = fd.substring(0,n); 
13049             var rm = fd.substring(n); 
13050             if (rm < 5) {
13051                 return ps[0] + '.' + r;
13052             }
13053             r*=1; // turn it into a number;
13054             r++;
13055             if (String(r).length != n) {
13056                 ps[0]*=1;
13057                 ps[0]++;
13058                 r = String(r).substring(1); // chop the end off.
13059             }
13060             
13061             return ps[0] + '.' + r;
13062              
13063         },
13064         
13065         /**
13066          * Format a number as US currency
13067          * @param {Number/String} value The numeric value to format
13068          * @return {String} The formatted currency string
13069          */
13070         usMoney : function(v){
13071             v = (Math.round((v-0)*100))/100;
13072             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13073             v = String(v);
13074             var ps = v.split('.');
13075             var whole = ps[0];
13076             var sub = ps[1] ? '.'+ ps[1] : '.00';
13077             var r = /(\d+)(\d{3})/;
13078             while (r.test(whole)) {
13079                 whole = whole.replace(r, '$1' + ',' + '$2');
13080             }
13081             return "$" + whole + sub ;
13082         },
13083         
13084         /**
13085          * Parse a value into a formatted date using the specified format pattern.
13086          * @param {Mixed} value The value to format
13087          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13088          * @return {String} The formatted date string
13089          */
13090         date : function(v, format){
13091             if(!v){
13092                 return "";
13093             }
13094             if(!(v instanceof Date)){
13095                 v = new Date(Date.parse(v));
13096             }
13097             return v.dateFormat(format || "m/d/Y");
13098         },
13099
13100         /**
13101          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13102          * @param {String} format Any valid date format string
13103          * @return {Function} The date formatting function
13104          */
13105         dateRenderer : function(format){
13106             return function(v){
13107                 return Roo.util.Format.date(v, format);  
13108             };
13109         },
13110
13111         // private
13112         stripTagsRE : /<\/?[^>]+>/gi,
13113         
13114         /**
13115          * Strips all HTML tags
13116          * @param {Mixed} value The text from which to strip tags
13117          * @return {String} The stripped text
13118          */
13119         stripTags : function(v){
13120             return !v ? v : String(v).replace(this.stripTagsRE, "");
13121         }
13122     };
13123 }();/*
13124  * Based on:
13125  * Ext JS Library 1.1.1
13126  * Copyright(c) 2006-2007, Ext JS, LLC.
13127  *
13128  * Originally Released Under LGPL - original licence link has changed is not relivant.
13129  *
13130  * Fork - LGPL
13131  * <script type="text/javascript">
13132  */
13133
13134
13135  
13136
13137 /**
13138  * @class Roo.MasterTemplate
13139  * @extends Roo.Template
13140  * Provides a template that can have child templates. The syntax is:
13141 <pre><code>
13142 var t = new Roo.MasterTemplate(
13143         '&lt;select name="{name}"&gt;',
13144                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13145         '&lt;/select&gt;'
13146 );
13147 t.add('options', {value: 'foo', text: 'bar'});
13148 // or you can add multiple child elements in one shot
13149 t.addAll('options', [
13150     {value: 'foo', text: 'bar'},
13151     {value: 'foo2', text: 'bar2'},
13152     {value: 'foo3', text: 'bar3'}
13153 ]);
13154 // then append, applying the master template values
13155 t.append('my-form', {name: 'my-select'});
13156 </code></pre>
13157 * A name attribute for the child template is not required if you have only one child
13158 * template or you want to refer to them by index.
13159  */
13160 Roo.MasterTemplate = function(){
13161     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13162     this.originalHtml = this.html;
13163     var st = {};
13164     var m, re = this.subTemplateRe;
13165     re.lastIndex = 0;
13166     var subIndex = 0;
13167     while(m = re.exec(this.html)){
13168         var name = m[1], content = m[2];
13169         st[subIndex] = {
13170             name: name,
13171             index: subIndex,
13172             buffer: [],
13173             tpl : new Roo.Template(content)
13174         };
13175         if(name){
13176             st[name] = st[subIndex];
13177         }
13178         st[subIndex].tpl.compile();
13179         st[subIndex].tpl.call = this.call.createDelegate(this);
13180         subIndex++;
13181     }
13182     this.subCount = subIndex;
13183     this.subs = st;
13184 };
13185 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13186     /**
13187     * The regular expression used to match sub templates
13188     * @type RegExp
13189     * @property
13190     */
13191     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13192
13193     /**
13194      * Applies the passed values to a child template.
13195      * @param {String/Number} name (optional) The name or index of the child template
13196      * @param {Array/Object} values The values to be applied to the template
13197      * @return {MasterTemplate} this
13198      */
13199      add : function(name, values){
13200         if(arguments.length == 1){
13201             values = arguments[0];
13202             name = 0;
13203         }
13204         var s = this.subs[name];
13205         s.buffer[s.buffer.length] = s.tpl.apply(values);
13206         return this;
13207     },
13208
13209     /**
13210      * Applies all the passed values to a child template.
13211      * @param {String/Number} name (optional) The name or index of the child template
13212      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13213      * @param {Boolean} reset (optional) True to reset the template first
13214      * @return {MasterTemplate} this
13215      */
13216     fill : function(name, values, reset){
13217         var a = arguments;
13218         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13219             values = a[0];
13220             name = 0;
13221             reset = a[1];
13222         }
13223         if(reset){
13224             this.reset();
13225         }
13226         for(var i = 0, len = values.length; i < len; i++){
13227             this.add(name, values[i]);
13228         }
13229         return this;
13230     },
13231
13232     /**
13233      * Resets the template for reuse
13234      * @return {MasterTemplate} this
13235      */
13236      reset : function(){
13237         var s = this.subs;
13238         for(var i = 0; i < this.subCount; i++){
13239             s[i].buffer = [];
13240         }
13241         return this;
13242     },
13243
13244     applyTemplate : function(values){
13245         var s = this.subs;
13246         var replaceIndex = -1;
13247         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13248             return s[++replaceIndex].buffer.join("");
13249         });
13250         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13251     },
13252
13253     apply : function(){
13254         return this.applyTemplate.apply(this, arguments);
13255     },
13256
13257     compile : function(){return this;}
13258 });
13259
13260 /**
13261  * Alias for fill().
13262  * @method
13263  */
13264 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13265  /**
13266  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13267  * var tpl = Roo.MasterTemplate.from('element-id');
13268  * @param {String/HTMLElement} el
13269  * @param {Object} config
13270  * @static
13271  */
13272 Roo.MasterTemplate.from = function(el, config){
13273     el = Roo.getDom(el);
13274     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13275 };/*
13276  * Based on:
13277  * Ext JS Library 1.1.1
13278  * Copyright(c) 2006-2007, Ext JS, LLC.
13279  *
13280  * Originally Released Under LGPL - original licence link has changed is not relivant.
13281  *
13282  * Fork - LGPL
13283  * <script type="text/javascript">
13284  */
13285
13286  
13287 /**
13288  * @class Roo.util.CSS
13289  * Utility class for manipulating CSS rules
13290  * @singleton
13291  */
13292 Roo.util.CSS = function(){
13293         var rules = null;
13294         var doc = document;
13295
13296     var camelRe = /(-[a-z])/gi;
13297     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13298
13299    return {
13300    /**
13301     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13302     * tag and appended to the HEAD of the document.
13303     * @param {String|Object} cssText The text containing the css rules
13304     * @param {String} id An id to add to the stylesheet for later removal
13305     * @return {StyleSheet}
13306     */
13307     createStyleSheet : function(cssText, id){
13308         var ss;
13309         var head = doc.getElementsByTagName("head")[0];
13310         var nrules = doc.createElement("style");
13311         nrules.setAttribute("type", "text/css");
13312         if(id){
13313             nrules.setAttribute("id", id);
13314         }
13315         if (typeof(cssText) != 'string') {
13316             // support object maps..
13317             // not sure if this a good idea.. 
13318             // perhaps it should be merged with the general css handling
13319             // and handle js style props.
13320             var cssTextNew = [];
13321             for(var n in cssText) {
13322                 var citems = [];
13323                 for(var k in cssText[n]) {
13324                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13325                 }
13326                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13327                 
13328             }
13329             cssText = cssTextNew.join("\n");
13330             
13331         }
13332        
13333        
13334        if(Roo.isIE){
13335            head.appendChild(nrules);
13336            ss = nrules.styleSheet;
13337            ss.cssText = cssText;
13338        }else{
13339            try{
13340                 nrules.appendChild(doc.createTextNode(cssText));
13341            }catch(e){
13342                nrules.cssText = cssText; 
13343            }
13344            head.appendChild(nrules);
13345            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13346        }
13347        this.cacheStyleSheet(ss);
13348        return ss;
13349    },
13350
13351    /**
13352     * Removes a style or link tag by id
13353     * @param {String} id The id of the tag
13354     */
13355    removeStyleSheet : function(id){
13356        var existing = doc.getElementById(id);
13357        if(existing){
13358            existing.parentNode.removeChild(existing);
13359        }
13360    },
13361
13362    /**
13363     * Dynamically swaps an existing stylesheet reference for a new one
13364     * @param {String} id The id of an existing link tag to remove
13365     * @param {String} url The href of the new stylesheet to include
13366     */
13367    swapStyleSheet : function(id, url){
13368        this.removeStyleSheet(id);
13369        var ss = doc.createElement("link");
13370        ss.setAttribute("rel", "stylesheet");
13371        ss.setAttribute("type", "text/css");
13372        ss.setAttribute("id", id);
13373        ss.setAttribute("href", url);
13374        doc.getElementsByTagName("head")[0].appendChild(ss);
13375    },
13376    
13377    /**
13378     * Refresh the rule cache if you have dynamically added stylesheets
13379     * @return {Object} An object (hash) of rules indexed by selector
13380     */
13381    refreshCache : function(){
13382        return this.getRules(true);
13383    },
13384
13385    // private
13386    cacheStyleSheet : function(stylesheet){
13387        if(!rules){
13388            rules = {};
13389        }
13390        try{// try catch for cross domain access issue
13391            var ssRules = stylesheet.cssRules || stylesheet.rules;
13392            for(var j = ssRules.length-1; j >= 0; --j){
13393                rules[ssRules[j].selectorText] = ssRules[j];
13394            }
13395        }catch(e){}
13396    },
13397    
13398    /**
13399     * Gets all css rules for the document
13400     * @param {Boolean} refreshCache true to refresh the internal cache
13401     * @return {Object} An object (hash) of rules indexed by selector
13402     */
13403    getRules : function(refreshCache){
13404                 if(rules == null || refreshCache){
13405                         rules = {};
13406                         var ds = doc.styleSheets;
13407                         for(var i =0, len = ds.length; i < len; i++){
13408                             try{
13409                         this.cacheStyleSheet(ds[i]);
13410                     }catch(e){} 
13411                 }
13412                 }
13413                 return rules;
13414         },
13415         
13416         /**
13417     * Gets an an individual CSS rule by selector(s)
13418     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13419     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13420     * @return {CSSRule} The CSS rule or null if one is not found
13421     */
13422    getRule : function(selector, refreshCache){
13423                 var rs = this.getRules(refreshCache);
13424                 if(!(selector instanceof Array)){
13425                     return rs[selector];
13426                 }
13427                 for(var i = 0; i < selector.length; i++){
13428                         if(rs[selector[i]]){
13429                                 return rs[selector[i]];
13430                         }
13431                 }
13432                 return null;
13433         },
13434         
13435         
13436         /**
13437     * Updates a rule property
13438     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13439     * @param {String} property The css property
13440     * @param {String} value The new value for the property
13441     * @return {Boolean} true If a rule was found and updated
13442     */
13443    updateRule : function(selector, property, value){
13444                 if(!(selector instanceof Array)){
13445                         var rule = this.getRule(selector);
13446                         if(rule){
13447                                 rule.style[property.replace(camelRe, camelFn)] = value;
13448                                 return true;
13449                         }
13450                 }else{
13451                         for(var i = 0; i < selector.length; i++){
13452                                 if(this.updateRule(selector[i], property, value)){
13453                                         return true;
13454                                 }
13455                         }
13456                 }
13457                 return false;
13458         }
13459    };   
13460 }();/*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470
13471  
13472
13473 /**
13474  * @class Roo.util.ClickRepeater
13475  * @extends Roo.util.Observable
13476  * 
13477  * A wrapper class which can be applied to any element. Fires a "click" event while the
13478  * mouse is pressed. The interval between firings may be specified in the config but
13479  * defaults to 10 milliseconds.
13480  * 
13481  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13482  * 
13483  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13484  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13485  * Similar to an autorepeat key delay.
13486  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13487  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13488  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13489  *           "interval" and "delay" are ignored. "immediate" is honored.
13490  * @cfg {Boolean} preventDefault True to prevent the default click event
13491  * @cfg {Boolean} stopDefault True to stop the default click event
13492  * 
13493  * @history
13494  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13495  *     2007-02-02 jvs Renamed to ClickRepeater
13496  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13497  *
13498  *  @constructor
13499  * @param {String/HTMLElement/Element} el The element to listen on
13500  * @param {Object} config
13501  **/
13502 Roo.util.ClickRepeater = function(el, config)
13503 {
13504     this.el = Roo.get(el);
13505     this.el.unselectable();
13506
13507     Roo.apply(this, config);
13508
13509     this.addEvents({
13510     /**
13511      * @event mousedown
13512      * Fires when the mouse button is depressed.
13513      * @param {Roo.util.ClickRepeater} this
13514      */
13515         "mousedown" : true,
13516     /**
13517      * @event click
13518      * Fires on a specified interval during the time the element is pressed.
13519      * @param {Roo.util.ClickRepeater} this
13520      */
13521         "click" : true,
13522     /**
13523      * @event mouseup
13524      * Fires when the mouse key is released.
13525      * @param {Roo.util.ClickRepeater} this
13526      */
13527         "mouseup" : true
13528     });
13529
13530     this.el.on("mousedown", this.handleMouseDown, this);
13531     if(this.preventDefault || this.stopDefault){
13532         this.el.on("click", function(e){
13533             if(this.preventDefault){
13534                 e.preventDefault();
13535             }
13536             if(this.stopDefault){
13537                 e.stopEvent();
13538             }
13539         }, this);
13540     }
13541
13542     // allow inline handler
13543     if(this.handler){
13544         this.on("click", this.handler,  this.scope || this);
13545     }
13546
13547     Roo.util.ClickRepeater.superclass.constructor.call(this);
13548 };
13549
13550 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13551     interval : 20,
13552     delay: 250,
13553     preventDefault : true,
13554     stopDefault : false,
13555     timer : 0,
13556
13557     // private
13558     handleMouseDown : function(){
13559         clearTimeout(this.timer);
13560         this.el.blur();
13561         if(this.pressClass){
13562             this.el.addClass(this.pressClass);
13563         }
13564         this.mousedownTime = new Date();
13565
13566         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13567         this.el.on("mouseout", this.handleMouseOut, this);
13568
13569         this.fireEvent("mousedown", this);
13570         this.fireEvent("click", this);
13571         
13572         this.timer = this.click.defer(this.delay || this.interval, this);
13573     },
13574
13575     // private
13576     click : function(){
13577         this.fireEvent("click", this);
13578         this.timer = this.click.defer(this.getInterval(), this);
13579     },
13580
13581     // private
13582     getInterval: function(){
13583         if(!this.accelerate){
13584             return this.interval;
13585         }
13586         var pressTime = this.mousedownTime.getElapsed();
13587         if(pressTime < 500){
13588             return 400;
13589         }else if(pressTime < 1700){
13590             return 320;
13591         }else if(pressTime < 2600){
13592             return 250;
13593         }else if(pressTime < 3500){
13594             return 180;
13595         }else if(pressTime < 4400){
13596             return 140;
13597         }else if(pressTime < 5300){
13598             return 80;
13599         }else if(pressTime < 6200){
13600             return 50;
13601         }else{
13602             return 10;
13603         }
13604     },
13605
13606     // private
13607     handleMouseOut : function(){
13608         clearTimeout(this.timer);
13609         if(this.pressClass){
13610             this.el.removeClass(this.pressClass);
13611         }
13612         this.el.on("mouseover", this.handleMouseReturn, this);
13613     },
13614
13615     // private
13616     handleMouseReturn : function(){
13617         this.el.un("mouseover", this.handleMouseReturn);
13618         if(this.pressClass){
13619             this.el.addClass(this.pressClass);
13620         }
13621         this.click();
13622     },
13623
13624     // private
13625     handleMouseUp : function(){
13626         clearTimeout(this.timer);
13627         this.el.un("mouseover", this.handleMouseReturn);
13628         this.el.un("mouseout", this.handleMouseOut);
13629         Roo.get(document).un("mouseup", this.handleMouseUp);
13630         this.el.removeClass(this.pressClass);
13631         this.fireEvent("mouseup", this);
13632     }
13633 });/*
13634  * Based on:
13635  * Ext JS Library 1.1.1
13636  * Copyright(c) 2006-2007, Ext JS, LLC.
13637  *
13638  * Originally Released Under LGPL - original licence link has changed is not relivant.
13639  *
13640  * Fork - LGPL
13641  * <script type="text/javascript">
13642  */
13643
13644  
13645 /**
13646  * @class Roo.KeyNav
13647  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13648  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13649  * way to implement custom navigation schemes for any UI component.</p>
13650  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13651  * pageUp, pageDown, del, home, end.  Usage:</p>
13652  <pre><code>
13653 var nav = new Roo.KeyNav("my-element", {
13654     "left" : function(e){
13655         this.moveLeft(e.ctrlKey);
13656     },
13657     "right" : function(e){
13658         this.moveRight(e.ctrlKey);
13659     },
13660     "enter" : function(e){
13661         this.save();
13662     },
13663     scope : this
13664 });
13665 </code></pre>
13666  * @constructor
13667  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13668  * @param {Object} config The config
13669  */
13670 Roo.KeyNav = function(el, config){
13671     this.el = Roo.get(el);
13672     Roo.apply(this, config);
13673     if(!this.disabled){
13674         this.disabled = true;
13675         this.enable();
13676     }
13677 };
13678
13679 Roo.KeyNav.prototype = {
13680     /**
13681      * @cfg {Boolean} disabled
13682      * True to disable this KeyNav instance (defaults to false)
13683      */
13684     disabled : false,
13685     /**
13686      * @cfg {String} defaultEventAction
13687      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13688      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13689      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13690      */
13691     defaultEventAction: "stopEvent",
13692     /**
13693      * @cfg {Boolean} forceKeyDown
13694      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13695      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13696      * handle keydown instead of keypress.
13697      */
13698     forceKeyDown : false,
13699
13700     // private
13701     prepareEvent : function(e){
13702         var k = e.getKey();
13703         var h = this.keyToHandler[k];
13704         //if(h && this[h]){
13705         //    e.stopPropagation();
13706         //}
13707         if(Roo.isSafari && h && k >= 37 && k <= 40){
13708             e.stopEvent();
13709         }
13710     },
13711
13712     // private
13713     relay : function(e){
13714         var k = e.getKey();
13715         var h = this.keyToHandler[k];
13716         if(h && this[h]){
13717             if(this.doRelay(e, this[h], h) !== true){
13718                 e[this.defaultEventAction]();
13719             }
13720         }
13721     },
13722
13723     // private
13724     doRelay : function(e, h, hname){
13725         return h.call(this.scope || this, e);
13726     },
13727
13728     // possible handlers
13729     enter : false,
13730     left : false,
13731     right : false,
13732     up : false,
13733     down : false,
13734     tab : false,
13735     esc : false,
13736     pageUp : false,
13737     pageDown : false,
13738     del : false,
13739     home : false,
13740     end : false,
13741
13742     // quick lookup hash
13743     keyToHandler : {
13744         37 : "left",
13745         39 : "right",
13746         38 : "up",
13747         40 : "down",
13748         33 : "pageUp",
13749         34 : "pageDown",
13750         46 : "del",
13751         36 : "home",
13752         35 : "end",
13753         13 : "enter",
13754         27 : "esc",
13755         9  : "tab"
13756     },
13757
13758         /**
13759          * Enable this KeyNav
13760          */
13761         enable: function(){
13762                 if(this.disabled){
13763             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13764             // the EventObject will normalize Safari automatically
13765             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13766                 this.el.on("keydown", this.relay,  this);
13767             }else{
13768                 this.el.on("keydown", this.prepareEvent,  this);
13769                 this.el.on("keypress", this.relay,  this);
13770             }
13771                     this.disabled = false;
13772                 }
13773         },
13774
13775         /**
13776          * Disable this KeyNav
13777          */
13778         disable: function(){
13779                 if(!this.disabled){
13780                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.un("keydown", this.relay);
13782             }else{
13783                 this.el.un("keydown", this.prepareEvent);
13784                 this.el.un("keypress", this.relay);
13785             }
13786                     this.disabled = true;
13787                 }
13788         }
13789 };/*
13790  * Based on:
13791  * Ext JS Library 1.1.1
13792  * Copyright(c) 2006-2007, Ext JS, LLC.
13793  *
13794  * Originally Released Under LGPL - original licence link has changed is not relivant.
13795  *
13796  * Fork - LGPL
13797  * <script type="text/javascript">
13798  */
13799
13800  
13801 /**
13802  * @class Roo.KeyMap
13803  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13804  * The constructor accepts the same config object as defined by {@link #addBinding}.
13805  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13806  * combination it will call the function with this signature (if the match is a multi-key
13807  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13808  * A KeyMap can also handle a string representation of keys.<br />
13809  * Usage:
13810  <pre><code>
13811 // map one key by key code
13812 var map = new Roo.KeyMap("my-element", {
13813     key: 13, // or Roo.EventObject.ENTER
13814     fn: myHandler,
13815     scope: myObject
13816 });
13817
13818 // map multiple keys to one action by string
13819 var map = new Roo.KeyMap("my-element", {
13820     key: "a\r\n\t",
13821     fn: myHandler,
13822     scope: myObject
13823 });
13824
13825 // map multiple keys to multiple actions by strings and array of codes
13826 var map = new Roo.KeyMap("my-element", [
13827     {
13828         key: [10,13],
13829         fn: function(){ alert("Return was pressed"); }
13830     }, {
13831         key: "abc",
13832         fn: function(){ alert('a, b or c was pressed'); }
13833     }, {
13834         key: "\t",
13835         ctrl:true,
13836         shift:true,
13837         fn: function(){ alert('Control + shift + tab was pressed.'); }
13838     }
13839 ]);
13840 </code></pre>
13841  * <b>Note: A KeyMap starts enabled</b>
13842  * @constructor
13843  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13844  * @param {Object} config The config (see {@link #addBinding})
13845  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13846  */
13847 Roo.KeyMap = function(el, config, eventName){
13848     this.el  = Roo.get(el);
13849     this.eventName = eventName || "keydown";
13850     this.bindings = [];
13851     if(config){
13852         this.addBinding(config);
13853     }
13854     this.enable();
13855 };
13856
13857 Roo.KeyMap.prototype = {
13858     /**
13859      * True to stop the event from bubbling and prevent the default browser action if the
13860      * key was handled by the KeyMap (defaults to false)
13861      * @type Boolean
13862      */
13863     stopEvent : false,
13864
13865     /**
13866      * Add a new binding to this KeyMap. The following config object properties are supported:
13867      * <pre>
13868 Property    Type             Description
13869 ----------  ---------------  ----------------------------------------------------------------------
13870 key         String/Array     A single keycode or an array of keycodes to handle
13871 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13872 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13873 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13874 fn          Function         The function to call when KeyMap finds the expected key combination
13875 scope       Object           The scope of the callback function
13876 </pre>
13877      *
13878      * Usage:
13879      * <pre><code>
13880 // Create a KeyMap
13881 var map = new Roo.KeyMap(document, {
13882     key: Roo.EventObject.ENTER,
13883     fn: handleKey,
13884     scope: this
13885 });
13886
13887 //Add a new binding to the existing KeyMap later
13888 map.addBinding({
13889     key: 'abc',
13890     shift: true,
13891     fn: handleKey,
13892     scope: this
13893 });
13894 </code></pre>
13895      * @param {Object/Array} config A single KeyMap config or an array of configs
13896      */
13897         addBinding : function(config){
13898         if(config instanceof Array){
13899             for(var i = 0, len = config.length; i < len; i++){
13900                 this.addBinding(config[i]);
13901             }
13902             return;
13903         }
13904         var keyCode = config.key,
13905             shift = config.shift, 
13906             ctrl = config.ctrl, 
13907             alt = config.alt,
13908             fn = config.fn,
13909             scope = config.scope;
13910         if(typeof keyCode == "string"){
13911             var ks = [];
13912             var keyString = keyCode.toUpperCase();
13913             for(var j = 0, len = keyString.length; j < len; j++){
13914                 ks.push(keyString.charCodeAt(j));
13915             }
13916             keyCode = ks;
13917         }
13918         var keyArray = keyCode instanceof Array;
13919         var handler = function(e){
13920             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13921                 var k = e.getKey();
13922                 if(keyArray){
13923                     for(var i = 0, len = keyCode.length; i < len; i++){
13924                         if(keyCode[i] == k){
13925                           if(this.stopEvent){
13926                               e.stopEvent();
13927                           }
13928                           fn.call(scope || window, k, e);
13929                           return;
13930                         }
13931                     }
13932                 }else{
13933                     if(k == keyCode){
13934                         if(this.stopEvent){
13935                            e.stopEvent();
13936                         }
13937                         fn.call(scope || window, k, e);
13938                     }
13939                 }
13940             }
13941         };
13942         this.bindings.push(handler);  
13943         },
13944
13945     /**
13946      * Shorthand for adding a single key listener
13947      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13948      * following options:
13949      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13950      * @param {Function} fn The function to call
13951      * @param {Object} scope (optional) The scope of the function
13952      */
13953     on : function(key, fn, scope){
13954         var keyCode, shift, ctrl, alt;
13955         if(typeof key == "object" && !(key instanceof Array)){
13956             keyCode = key.key;
13957             shift = key.shift;
13958             ctrl = key.ctrl;
13959             alt = key.alt;
13960         }else{
13961             keyCode = key;
13962         }
13963         this.addBinding({
13964             key: keyCode,
13965             shift: shift,
13966             ctrl: ctrl,
13967             alt: alt,
13968             fn: fn,
13969             scope: scope
13970         })
13971     },
13972
13973     // private
13974     handleKeyDown : function(e){
13975             if(this.enabled){ //just in case
13976             var b = this.bindings;
13977             for(var i = 0, len = b.length; i < len; i++){
13978                 b[i].call(this, e);
13979             }
13980             }
13981         },
13982         
13983         /**
13984          * Returns true if this KeyMap is enabled
13985          * @return {Boolean} 
13986          */
13987         isEnabled : function(){
13988             return this.enabled;  
13989         },
13990         
13991         /**
13992          * Enables this KeyMap
13993          */
13994         enable: function(){
13995                 if(!this.enabled){
13996                     this.el.on(this.eventName, this.handleKeyDown, this);
13997                     this.enabled = true;
13998                 }
13999         },
14000
14001         /**
14002          * Disable this KeyMap
14003          */
14004         disable: function(){
14005                 if(this.enabled){
14006                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14007                     this.enabled = false;
14008                 }
14009         }
14010 };/*
14011  * Based on:
14012  * Ext JS Library 1.1.1
14013  * Copyright(c) 2006-2007, Ext JS, LLC.
14014  *
14015  * Originally Released Under LGPL - original licence link has changed is not relivant.
14016  *
14017  * Fork - LGPL
14018  * <script type="text/javascript">
14019  */
14020
14021  
14022 /**
14023  * @class Roo.util.TextMetrics
14024  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14025  * wide, in pixels, a given block of text will be.
14026  * @singleton
14027  */
14028 Roo.util.TextMetrics = function(){
14029     var shared;
14030     return {
14031         /**
14032          * Measures the size of the specified text
14033          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14034          * that can affect the size of the rendered text
14035          * @param {String} text The text to measure
14036          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14037          * in order to accurately measure the text height
14038          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14039          */
14040         measure : function(el, text, fixedWidth){
14041             if(!shared){
14042                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14043             }
14044             shared.bind(el);
14045             shared.setFixedWidth(fixedWidth || 'auto');
14046             return shared.getSize(text);
14047         },
14048
14049         /**
14050          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14051          * the overhead of multiple calls to initialize the style properties on each measurement.
14052          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14053          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14054          * in order to accurately measure the text height
14055          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14056          */
14057         createInstance : function(el, fixedWidth){
14058             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14059         }
14060     };
14061 }();
14062
14063  
14064
14065 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14066     var ml = new Roo.Element(document.createElement('div'));
14067     document.body.appendChild(ml.dom);
14068     ml.position('absolute');
14069     ml.setLeftTop(-1000, -1000);
14070     ml.hide();
14071
14072     if(fixedWidth){
14073         ml.setWidth(fixedWidth);
14074     }
14075      
14076     var instance = {
14077         /**
14078          * Returns the size of the specified text based on the internal element's style and width properties
14079          * @memberOf Roo.util.TextMetrics.Instance#
14080          * @param {String} text The text to measure
14081          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14082          */
14083         getSize : function(text){
14084             ml.update(text);
14085             var s = ml.getSize();
14086             ml.update('');
14087             return s;
14088         },
14089
14090         /**
14091          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14092          * that can affect the size of the rendered text
14093          * @memberOf Roo.util.TextMetrics.Instance#
14094          * @param {String/HTMLElement} el The element, dom node or id
14095          */
14096         bind : function(el){
14097             ml.setStyle(
14098                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14099             );
14100         },
14101
14102         /**
14103          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14104          * to set a fixed width in order to accurately measure the text height.
14105          * @memberOf Roo.util.TextMetrics.Instance#
14106          * @param {Number} width The width to set on the element
14107          */
14108         setFixedWidth : function(width){
14109             ml.setWidth(width);
14110         },
14111
14112         /**
14113          * Returns the measured width of the specified text
14114          * @memberOf Roo.util.TextMetrics.Instance#
14115          * @param {String} text The text to measure
14116          * @return {Number} width The width in pixels
14117          */
14118         getWidth : function(text){
14119             ml.dom.style.width = 'auto';
14120             return this.getSize(text).width;
14121         },
14122
14123         /**
14124          * Returns the measured height of the specified text.  For multiline text, be sure to call
14125          * {@link #setFixedWidth} if necessary.
14126          * @memberOf Roo.util.TextMetrics.Instance#
14127          * @param {String} text The text to measure
14128          * @return {Number} height The height in pixels
14129          */
14130         getHeight : function(text){
14131             return this.getSize(text).height;
14132         }
14133     };
14134
14135     instance.bind(bindTo);
14136
14137     return instance;
14138 };
14139
14140 // backwards compat
14141 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151
14152 /**
14153  * @class Roo.state.Provider
14154  * Abstract base class for state provider implementations. This class provides methods
14155  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14156  * Provider interface.
14157  */
14158 Roo.state.Provider = function(){
14159     /**
14160      * @event statechange
14161      * Fires when a state change occurs.
14162      * @param {Provider} this This state provider
14163      * @param {String} key The state key which was changed
14164      * @param {String} value The encoded value for the state
14165      */
14166     this.addEvents({
14167         "statechange": true
14168     });
14169     this.state = {};
14170     Roo.state.Provider.superclass.constructor.call(this);
14171 };
14172 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14173     /**
14174      * Returns the current value for a key
14175      * @param {String} name The key name
14176      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14177      * @return {Mixed} The state data
14178      */
14179     get : function(name, defaultValue){
14180         return typeof this.state[name] == "undefined" ?
14181             defaultValue : this.state[name];
14182     },
14183     
14184     /**
14185      * Clears a value from the state
14186      * @param {String} name The key name
14187      */
14188     clear : function(name){
14189         delete this.state[name];
14190         this.fireEvent("statechange", this, name, null);
14191     },
14192     
14193     /**
14194      * Sets the value for a key
14195      * @param {String} name The key name
14196      * @param {Mixed} value The value to set
14197      */
14198     set : function(name, value){
14199         this.state[name] = value;
14200         this.fireEvent("statechange", this, name, value);
14201     },
14202     
14203     /**
14204      * Decodes a string previously encoded with {@link #encodeValue}.
14205      * @param {String} value The value to decode
14206      * @return {Mixed} The decoded value
14207      */
14208     decodeValue : function(cookie){
14209         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14210         var matches = re.exec(unescape(cookie));
14211         if(!matches || !matches[1]) return; // non state cookie
14212         var type = matches[1];
14213         var v = matches[2];
14214         switch(type){
14215             case "n":
14216                 return parseFloat(v);
14217             case "d":
14218                 return new Date(Date.parse(v));
14219             case "b":
14220                 return (v == "1");
14221             case "a":
14222                 var all = [];
14223                 var values = v.split("^");
14224                 for(var i = 0, len = values.length; i < len; i++){
14225                     all.push(this.decodeValue(values[i]));
14226                 }
14227                 return all;
14228            case "o":
14229                 var all = {};
14230                 var values = v.split("^");
14231                 for(var i = 0, len = values.length; i < len; i++){
14232                     var kv = values[i].split("=");
14233                     all[kv[0]] = this.decodeValue(kv[1]);
14234                 }
14235                 return all;
14236            default:
14237                 return v;
14238         }
14239     },
14240     
14241     /**
14242      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14243      * @param {Mixed} value The value to encode
14244      * @return {String} The encoded value
14245      */
14246     encodeValue : function(v){
14247         var enc;
14248         if(typeof v == "number"){
14249             enc = "n:" + v;
14250         }else if(typeof v == "boolean"){
14251             enc = "b:" + (v ? "1" : "0");
14252         }else if(v instanceof Date){
14253             enc = "d:" + v.toGMTString();
14254         }else if(v instanceof Array){
14255             var flat = "";
14256             for(var i = 0, len = v.length; i < len; i++){
14257                 flat += this.encodeValue(v[i]);
14258                 if(i != len-1) flat += "^";
14259             }
14260             enc = "a:" + flat;
14261         }else if(typeof v == "object"){
14262             var flat = "";
14263             for(var key in v){
14264                 if(typeof v[key] != "function"){
14265                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14266                 }
14267             }
14268             enc = "o:" + flat.substring(0, flat.length-1);
14269         }else{
14270             enc = "s:" + v;
14271         }
14272         return escape(enc);        
14273     }
14274 });
14275
14276 /*
14277  * Based on:
14278  * Ext JS Library 1.1.1
14279  * Copyright(c) 2006-2007, Ext JS, LLC.
14280  *
14281  * Originally Released Under LGPL - original licence link has changed is not relivant.
14282  *
14283  * Fork - LGPL
14284  * <script type="text/javascript">
14285  */
14286 /**
14287  * @class Roo.state.Manager
14288  * This is the global state manager. By default all components that are "state aware" check this class
14289  * for state information if you don't pass them a custom state provider. In order for this class
14290  * to be useful, it must be initialized with a provider when your application initializes.
14291  <pre><code>
14292 // in your initialization function
14293 init : function(){
14294    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14295    ...
14296    // supposed you have a {@link Roo.BorderLayout}
14297    var layout = new Roo.BorderLayout(...);
14298    layout.restoreState();
14299    // or a {Roo.BasicDialog}
14300    var dialog = new Roo.BasicDialog(...);
14301    dialog.restoreState();
14302  </code></pre>
14303  * @singleton
14304  */
14305 Roo.state.Manager = function(){
14306     var provider = new Roo.state.Provider();
14307     
14308     return {
14309         /**
14310          * Configures the default state provider for your application
14311          * @param {Provider} stateProvider The state provider to set
14312          */
14313         setProvider : function(stateProvider){
14314             provider = stateProvider;
14315         },
14316         
14317         /**
14318          * Returns the current value for a key
14319          * @param {String} name The key name
14320          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14321          * @return {Mixed} The state data
14322          */
14323         get : function(key, defaultValue){
14324             return provider.get(key, defaultValue);
14325         },
14326         
14327         /**
14328          * Sets the value for a key
14329          * @param {String} name The key name
14330          * @param {Mixed} value The state data
14331          */
14332          set : function(key, value){
14333             provider.set(key, value);
14334         },
14335         
14336         /**
14337          * Clears a value from the state
14338          * @param {String} name The key name
14339          */
14340         clear : function(key){
14341             provider.clear(key);
14342         },
14343         
14344         /**
14345          * Gets the currently configured state provider
14346          * @return {Provider} The state provider
14347          */
14348         getProvider : function(){
14349             return provider;
14350         }
14351     };
14352 }();
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363 /**
14364  * @class Roo.state.CookieProvider
14365  * @extends Roo.state.Provider
14366  * The default Provider implementation which saves state via cookies.
14367  * <br />Usage:
14368  <pre><code>
14369    var cp = new Roo.state.CookieProvider({
14370        path: "/cgi-bin/",
14371        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14372        domain: "roojs.com"
14373    })
14374    Roo.state.Manager.setProvider(cp);
14375  </code></pre>
14376  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14377  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14378  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14379  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14380  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14381  * domain the page is running on including the 'www' like 'www.roojs.com')
14382  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14383  * @constructor
14384  * Create a new CookieProvider
14385  * @param {Object} config The configuration object
14386  */
14387 Roo.state.CookieProvider = function(config){
14388     Roo.state.CookieProvider.superclass.constructor.call(this);
14389     this.path = "/";
14390     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14391     this.domain = null;
14392     this.secure = false;
14393     Roo.apply(this, config);
14394     this.state = this.readCookies();
14395 };
14396
14397 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14398     // private
14399     set : function(name, value){
14400         if(typeof value == "undefined" || value === null){
14401             this.clear(name);
14402             return;
14403         }
14404         this.setCookie(name, value);
14405         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14406     },
14407
14408     // private
14409     clear : function(name){
14410         this.clearCookie(name);
14411         Roo.state.CookieProvider.superclass.clear.call(this, name);
14412     },
14413
14414     // private
14415     readCookies : function(){
14416         var cookies = {};
14417         var c = document.cookie + ";";
14418         var re = /\s?(.*?)=(.*?);/g;
14419         var matches;
14420         while((matches = re.exec(c)) != null){
14421             var name = matches[1];
14422             var value = matches[2];
14423             if(name && name.substring(0,3) == "ys-"){
14424                 cookies[name.substr(3)] = this.decodeValue(value);
14425             }
14426         }
14427         return cookies;
14428     },
14429
14430     // private
14431     setCookie : function(name, value){
14432         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14433            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14434            ((this.path == null) ? "" : ("; path=" + this.path)) +
14435            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14436            ((this.secure == true) ? "; secure" : "");
14437     },
14438
14439     // private
14440     clearCookie : function(name){
14441         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14442            ((this.path == null) ? "" : ("; path=" + this.path)) +
14443            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14444            ((this.secure == true) ? "; secure" : "");
14445     }
14446 });/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457
14458
14459 /*
14460  * These classes are derivatives of the similarly named classes in the YUI Library.
14461  * The original license:
14462  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14463  * Code licensed under the BSD License:
14464  * http://developer.yahoo.net/yui/license.txt
14465  */
14466
14467 (function() {
14468
14469 var Event=Roo.EventManager;
14470 var Dom=Roo.lib.Dom;
14471
14472 /**
14473  * @class Roo.dd.DragDrop
14474  * @extends Roo.util.Observable
14475  * Defines the interface and base operation of items that that can be
14476  * dragged or can be drop targets.  It was designed to be extended, overriding
14477  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14478  * Up to three html elements can be associated with a DragDrop instance:
14479  * <ul>
14480  * <li>linked element: the element that is passed into the constructor.
14481  * This is the element which defines the boundaries for interaction with
14482  * other DragDrop objects.</li>
14483  * <li>handle element(s): The drag operation only occurs if the element that
14484  * was clicked matches a handle element.  By default this is the linked
14485  * element, but there are times that you will want only a portion of the
14486  * linked element to initiate the drag operation, and the setHandleElId()
14487  * method provides a way to define this.</li>
14488  * <li>drag element: this represents the element that would be moved along
14489  * with the cursor during a drag operation.  By default, this is the linked
14490  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14491  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14492  * </li>
14493  * </ul>
14494  * This class should not be instantiated until the onload event to ensure that
14495  * the associated elements are available.
14496  * The following would define a DragDrop obj that would interact with any
14497  * other DragDrop obj in the "group1" group:
14498  * <pre>
14499  *  dd = new Roo.dd.DragDrop("div1", "group1");
14500  * </pre>
14501  * Since none of the event handlers have been implemented, nothing would
14502  * actually happen if you were to run the code above.  Normally you would
14503  * override this class or one of the default implementations, but you can
14504  * also override the methods you want on an instance of the class...
14505  * <pre>
14506  *  dd.onDragDrop = function(e, id) {
14507  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14508  *  }
14509  * </pre>
14510  * @constructor
14511  * @param {String} id of the element that is linked to this instance
14512  * @param {String} sGroup the group of related DragDrop objects
14513  * @param {object} config an object containing configurable attributes
14514  *                Valid properties for DragDrop:
14515  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14516  */
14517 Roo.dd.DragDrop = function(id, sGroup, config) {
14518     if (id) {
14519         this.init(id, sGroup, config);
14520     }
14521     
14522 };
14523
14524 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14525
14526     /**
14527      * The id of the element associated with this object.  This is what we
14528      * refer to as the "linked element" because the size and position of
14529      * this element is used to determine when the drag and drop objects have
14530      * interacted.
14531      * @property id
14532      * @type String
14533      */
14534     id: null,
14535
14536     /**
14537      * Configuration attributes passed into the constructor
14538      * @property config
14539      * @type object
14540      */
14541     config: null,
14542
14543     /**
14544      * The id of the element that will be dragged.  By default this is same
14545      * as the linked element , but could be changed to another element. Ex:
14546      * Roo.dd.DDProxy
14547      * @property dragElId
14548      * @type String
14549      * @private
14550      */
14551     dragElId: null,
14552
14553     /**
14554      * the id of the element that initiates the drag operation.  By default
14555      * this is the linked element, but could be changed to be a child of this
14556      * element.  This lets us do things like only starting the drag when the
14557      * header element within the linked html element is clicked.
14558      * @property handleElId
14559      * @type String
14560      * @private
14561      */
14562     handleElId: null,
14563
14564     /**
14565      * An associative array of HTML tags that will be ignored if clicked.
14566      * @property invalidHandleTypes
14567      * @type {string: string}
14568      */
14569     invalidHandleTypes: null,
14570
14571     /**
14572      * An associative array of ids for elements that will be ignored if clicked
14573      * @property invalidHandleIds
14574      * @type {string: string}
14575      */
14576     invalidHandleIds: null,
14577
14578     /**
14579      * An indexted array of css class names for elements that will be ignored
14580      * if clicked.
14581      * @property invalidHandleClasses
14582      * @type string[]
14583      */
14584     invalidHandleClasses: null,
14585
14586     /**
14587      * The linked element's absolute X position at the time the drag was
14588      * started
14589      * @property startPageX
14590      * @type int
14591      * @private
14592      */
14593     startPageX: 0,
14594
14595     /**
14596      * The linked element's absolute X position at the time the drag was
14597      * started
14598      * @property startPageY
14599      * @type int
14600      * @private
14601      */
14602     startPageY: 0,
14603
14604     /**
14605      * The group defines a logical collection of DragDrop objects that are
14606      * related.  Instances only get events when interacting with other
14607      * DragDrop object in the same group.  This lets us define multiple
14608      * groups using a single DragDrop subclass if we want.
14609      * @property groups
14610      * @type {string: string}
14611      */
14612     groups: null,
14613
14614     /**
14615      * Individual drag/drop instances can be locked.  This will prevent
14616      * onmousedown start drag.
14617      * @property locked
14618      * @type boolean
14619      * @private
14620      */
14621     locked: false,
14622
14623     /**
14624      * Lock this instance
14625      * @method lock
14626      */
14627     lock: function() { this.locked = true; },
14628
14629     /**
14630      * Unlock this instace
14631      * @method unlock
14632      */
14633     unlock: function() { this.locked = false; },
14634
14635     /**
14636      * By default, all insances can be a drop target.  This can be disabled by
14637      * setting isTarget to false.
14638      * @method isTarget
14639      * @type boolean
14640      */
14641     isTarget: true,
14642
14643     /**
14644      * The padding configured for this drag and drop object for calculating
14645      * the drop zone intersection with this object.
14646      * @method padding
14647      * @type int[]
14648      */
14649     padding: null,
14650
14651     /**
14652      * Cached reference to the linked element
14653      * @property _domRef
14654      * @private
14655      */
14656     _domRef: null,
14657
14658     /**
14659      * Internal typeof flag
14660      * @property __ygDragDrop
14661      * @private
14662      */
14663     __ygDragDrop: true,
14664
14665     /**
14666      * Set to true when horizontal contraints are applied
14667      * @property constrainX
14668      * @type boolean
14669      * @private
14670      */
14671     constrainX: false,
14672
14673     /**
14674      * Set to true when vertical contraints are applied
14675      * @property constrainY
14676      * @type boolean
14677      * @private
14678      */
14679     constrainY: false,
14680
14681     /**
14682      * The left constraint
14683      * @property minX
14684      * @type int
14685      * @private
14686      */
14687     minX: 0,
14688
14689     /**
14690      * The right constraint
14691      * @property maxX
14692      * @type int
14693      * @private
14694      */
14695     maxX: 0,
14696
14697     /**
14698      * The up constraint
14699      * @property minY
14700      * @type int
14701      * @type int
14702      * @private
14703      */
14704     minY: 0,
14705
14706     /**
14707      * The down constraint
14708      * @property maxY
14709      * @type int
14710      * @private
14711      */
14712     maxY: 0,
14713
14714     /**
14715      * Maintain offsets when we resetconstraints.  Set to true when you want
14716      * the position of the element relative to its parent to stay the same
14717      * when the page changes
14718      *
14719      * @property maintainOffset
14720      * @type boolean
14721      */
14722     maintainOffset: false,
14723
14724     /**
14725      * Array of pixel locations the element will snap to if we specified a
14726      * horizontal graduation/interval.  This array is generated automatically
14727      * when you define a tick interval.
14728      * @property xTicks
14729      * @type int[]
14730      */
14731     xTicks: null,
14732
14733     /**
14734      * Array of pixel locations the element will snap to if we specified a
14735      * vertical graduation/interval.  This array is generated automatically
14736      * when you define a tick interval.
14737      * @property yTicks
14738      * @type int[]
14739      */
14740     yTicks: null,
14741
14742     /**
14743      * By default the drag and drop instance will only respond to the primary
14744      * button click (left button for a right-handed mouse).  Set to true to
14745      * allow drag and drop to start with any mouse click that is propogated
14746      * by the browser
14747      * @property primaryButtonOnly
14748      * @type boolean
14749      */
14750     primaryButtonOnly: true,
14751
14752     /**
14753      * The availabe property is false until the linked dom element is accessible.
14754      * @property available
14755      * @type boolean
14756      */
14757     available: false,
14758
14759     /**
14760      * By default, drags can only be initiated if the mousedown occurs in the
14761      * region the linked element is.  This is done in part to work around a
14762      * bug in some browsers that mis-report the mousedown if the previous
14763      * mouseup happened outside of the window.  This property is set to true
14764      * if outer handles are defined.
14765      *
14766      * @property hasOuterHandles
14767      * @type boolean
14768      * @default false
14769      */
14770     hasOuterHandles: false,
14771
14772     /**
14773      * Code that executes immediately before the startDrag event
14774      * @method b4StartDrag
14775      * @private
14776      */
14777     b4StartDrag: function(x, y) { },
14778
14779     /**
14780      * Abstract method called after a drag/drop object is clicked
14781      * and the drag or mousedown time thresholds have beeen met.
14782      * @method startDrag
14783      * @param {int} X click location
14784      * @param {int} Y click location
14785      */
14786     startDrag: function(x, y) { /* override this */ },
14787
14788     /**
14789      * Code that executes immediately before the onDrag event
14790      * @method b4Drag
14791      * @private
14792      */
14793     b4Drag: function(e) { },
14794
14795     /**
14796      * Abstract method called during the onMouseMove event while dragging an
14797      * object.
14798      * @method onDrag
14799      * @param {Event} e the mousemove event
14800      */
14801     onDrag: function(e) { /* override this */ },
14802
14803     /**
14804      * Abstract method called when this element fist begins hovering over
14805      * another DragDrop obj
14806      * @method onDragEnter
14807      * @param {Event} e the mousemove event
14808      * @param {String|DragDrop[]} id In POINT mode, the element
14809      * id this is hovering over.  In INTERSECT mode, an array of one or more
14810      * dragdrop items being hovered over.
14811      */
14812     onDragEnter: function(e, id) { /* override this */ },
14813
14814     /**
14815      * Code that executes immediately before the onDragOver event
14816      * @method b4DragOver
14817      * @private
14818      */
14819     b4DragOver: function(e) { },
14820
14821     /**
14822      * Abstract method called when this element is hovering over another
14823      * DragDrop obj
14824      * @method onDragOver
14825      * @param {Event} e the mousemove event
14826      * @param {String|DragDrop[]} id In POINT mode, the element
14827      * id this is hovering over.  In INTERSECT mode, an array of dd items
14828      * being hovered over.
14829      */
14830     onDragOver: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOut event
14834      * @method b4DragOut
14835      * @private
14836      */
14837     b4DragOut: function(e) { },
14838
14839     /**
14840      * Abstract method called when we are no longer hovering over an element
14841      * @method onDragOut
14842      * @param {Event} e the mousemove event
14843      * @param {String|DragDrop[]} id In POINT mode, the element
14844      * id this was hovering over.  In INTERSECT mode, an array of dd items
14845      * that the mouse is no longer over.
14846      */
14847     onDragOut: function(e, id) { /* override this */ },
14848
14849     /**
14850      * Code that executes immediately before the onDragDrop event
14851      * @method b4DragDrop
14852      * @private
14853      */
14854     b4DragDrop: function(e) { },
14855
14856     /**
14857      * Abstract method called when this item is dropped on another DragDrop
14858      * obj
14859      * @method onDragDrop
14860      * @param {Event} e the mouseup event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14863      * was dropped on.
14864      */
14865     onDragDrop: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Abstract method called when this item is dropped on an area with no
14869      * drop target
14870      * @method onInvalidDrop
14871      * @param {Event} e the mouseup event
14872      */
14873     onInvalidDrop: function(e) { /* override this */ },
14874
14875     /**
14876      * Code that executes immediately before the endDrag event
14877      * @method b4EndDrag
14878      * @private
14879      */
14880     b4EndDrag: function(e) { },
14881
14882     /**
14883      * Fired when we are done dragging the object
14884      * @method endDrag
14885      * @param {Event} e the mouseup event
14886      */
14887     endDrag: function(e) { /* override this */ },
14888
14889     /**
14890      * Code executed immediately before the onMouseDown event
14891      * @method b4MouseDown
14892      * @param {Event} e the mousedown event
14893      * @private
14894      */
14895     b4MouseDown: function(e) {  },
14896
14897     /**
14898      * Event handler that fires when a drag/drop obj gets a mousedown
14899      * @method onMouseDown
14900      * @param {Event} e the mousedown event
14901      */
14902     onMouseDown: function(e) { /* override this */ },
14903
14904     /**
14905      * Event handler that fires when a drag/drop obj gets a mouseup
14906      * @method onMouseUp
14907      * @param {Event} e the mouseup event
14908      */
14909     onMouseUp: function(e) { /* override this */ },
14910
14911     /**
14912      * Override the onAvailable method to do what is needed after the initial
14913      * position was determined.
14914      * @method onAvailable
14915      */
14916     onAvailable: function () {
14917     },
14918
14919     /*
14920      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14921      * @type Object
14922      */
14923     defaultPadding : {left:0, right:0, top:0, bottom:0},
14924
14925     /*
14926      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14927  *
14928  * Usage:
14929  <pre><code>
14930  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14931                 { dragElId: "existingProxyDiv" });
14932  dd.startDrag = function(){
14933      this.constrainTo("parent-id");
14934  };
14935  </code></pre>
14936  * Or you can initalize it using the {@link Roo.Element} object:
14937  <pre><code>
14938  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14939      startDrag : function(){
14940          this.constrainTo("parent-id");
14941      }
14942  });
14943  </code></pre>
14944      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14945      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14946      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14947      * an object containing the sides to pad. For example: {right:10, bottom:10}
14948      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14949      */
14950     constrainTo : function(constrainTo, pad, inContent){
14951         if(typeof pad == "number"){
14952             pad = {left: pad, right:pad, top:pad, bottom:pad};
14953         }
14954         pad = pad || this.defaultPadding;
14955         var b = Roo.get(this.getEl()).getBox();
14956         var ce = Roo.get(constrainTo);
14957         var s = ce.getScroll();
14958         var c, cd = ce.dom;
14959         if(cd == document.body){
14960             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14961         }else{
14962             xy = ce.getXY();
14963             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14964         }
14965
14966
14967         var topSpace = b.y - c.y;
14968         var leftSpace = b.x - c.x;
14969
14970         this.resetConstraints();
14971         this.setXConstraint(leftSpace - (pad.left||0), // left
14972                 c.width - leftSpace - b.width - (pad.right||0) //right
14973         );
14974         this.setYConstraint(topSpace - (pad.top||0), //top
14975                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14976         );
14977     },
14978
14979     /**
14980      * Returns a reference to the linked element
14981      * @method getEl
14982      * @return {HTMLElement} the html element
14983      */
14984     getEl: function() {
14985         if (!this._domRef) {
14986             this._domRef = Roo.getDom(this.id);
14987         }
14988
14989         return this._domRef;
14990     },
14991
14992     /**
14993      * Returns a reference to the actual element to drag.  By default this is
14994      * the same as the html element, but it can be assigned to another
14995      * element. An example of this can be found in Roo.dd.DDProxy
14996      * @method getDragEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getDragEl: function() {
15000         return Roo.getDom(this.dragElId);
15001     },
15002
15003     /**
15004      * Sets up the DragDrop object.  Must be called in the constructor of any
15005      * Roo.dd.DragDrop subclass
15006      * @method init
15007      * @param id the id of the linked element
15008      * @param {String} sGroup the group of related items
15009      * @param {object} config configuration attributes
15010      */
15011     init: function(id, sGroup, config) {
15012         this.initTarget(id, sGroup, config);
15013         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15014         // Event.on(this.id, "selectstart", Event.preventDefault);
15015     },
15016
15017     /**
15018      * Initializes Targeting functionality only... the object does not
15019      * get a mousedown handler.
15020      * @method initTarget
15021      * @param id the id of the linked element
15022      * @param {String} sGroup the group of related items
15023      * @param {object} config configuration attributes
15024      */
15025     initTarget: function(id, sGroup, config) {
15026
15027         // configuration attributes
15028         this.config = config || {};
15029
15030         // create a local reference to the drag and drop manager
15031         this.DDM = Roo.dd.DDM;
15032         // initialize the groups array
15033         this.groups = {};
15034
15035         // assume that we have an element reference instead of an id if the
15036         // parameter is not a string
15037         if (typeof id !== "string") {
15038             id = Roo.id(id);
15039         }
15040
15041         // set the id
15042         this.id = id;
15043
15044         // add to an interaction group
15045         this.addToGroup((sGroup) ? sGroup : "default");
15046
15047         // We don't want to register this as the handle with the manager
15048         // so we just set the id rather than calling the setter.
15049         this.handleElId = id;
15050
15051         // the linked element is the element that gets dragged by default
15052         this.setDragElId(id);
15053
15054         // by default, clicked anchors will not start drag operations.
15055         this.invalidHandleTypes = { A: "A" };
15056         this.invalidHandleIds = {};
15057         this.invalidHandleClasses = [];
15058
15059         this.applyConfig();
15060
15061         this.handleOnAvailable();
15062     },
15063
15064     /**
15065      * Applies the configuration parameters that were passed into the constructor.
15066      * This is supposed to happen at each level through the inheritance chain.  So
15067      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15068      * DragDrop in order to get all of the parameters that are available in
15069      * each object.
15070      * @method applyConfig
15071      */
15072     applyConfig: function() {
15073
15074         // configurable properties:
15075         //    padding, isTarget, maintainOffset, primaryButtonOnly
15076         this.padding           = this.config.padding || [0, 0, 0, 0];
15077         this.isTarget          = (this.config.isTarget !== false);
15078         this.maintainOffset    = (this.config.maintainOffset);
15079         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15080
15081     },
15082
15083     /**
15084      * Executed when the linked element is available
15085      * @method handleOnAvailable
15086      * @private
15087      */
15088     handleOnAvailable: function() {
15089         this.available = true;
15090         this.resetConstraints();
15091         this.onAvailable();
15092     },
15093
15094      /**
15095      * Configures the padding for the target zone in px.  Effectively expands
15096      * (or reduces) the virtual object size for targeting calculations.
15097      * Supports css-style shorthand; if only one parameter is passed, all sides
15098      * will have that padding, and if only two are passed, the top and bottom
15099      * will have the first param, the left and right the second.
15100      * @method setPadding
15101      * @param {int} iTop    Top pad
15102      * @param {int} iRight  Right pad
15103      * @param {int} iBot    Bot pad
15104      * @param {int} iLeft   Left pad
15105      */
15106     setPadding: function(iTop, iRight, iBot, iLeft) {
15107         // this.padding = [iLeft, iRight, iTop, iBot];
15108         if (!iRight && 0 !== iRight) {
15109             this.padding = [iTop, iTop, iTop, iTop];
15110         } else if (!iBot && 0 !== iBot) {
15111             this.padding = [iTop, iRight, iTop, iRight];
15112         } else {
15113             this.padding = [iTop, iRight, iBot, iLeft];
15114         }
15115     },
15116
15117     /**
15118      * Stores the initial placement of the linked element.
15119      * @method setInitialPosition
15120      * @param {int} diffX   the X offset, default 0
15121      * @param {int} diffY   the Y offset, default 0
15122      */
15123     setInitPosition: function(diffX, diffY) {
15124         var el = this.getEl();
15125
15126         if (!this.DDM.verifyEl(el)) {
15127             return;
15128         }
15129
15130         var dx = diffX || 0;
15131         var dy = diffY || 0;
15132
15133         var p = Dom.getXY( el );
15134
15135         this.initPageX = p[0] - dx;
15136         this.initPageY = p[1] - dy;
15137
15138         this.lastPageX = p[0];
15139         this.lastPageY = p[1];
15140
15141
15142         this.setStartPosition(p);
15143     },
15144
15145     /**
15146      * Sets the start position of the element.  This is set when the obj
15147      * is initialized, the reset when a drag is started.
15148      * @method setStartPosition
15149      * @param pos current position (from previous lookup)
15150      * @private
15151      */
15152     setStartPosition: function(pos) {
15153         var p = pos || Dom.getXY( this.getEl() );
15154         this.deltaSetXY = null;
15155
15156         this.startPageX = p[0];
15157         this.startPageY = p[1];
15158     },
15159
15160     /**
15161      * Add this instance to a group of related drag/drop objects.  All
15162      * instances belong to at least one group, and can belong to as many
15163      * groups as needed.
15164      * @method addToGroup
15165      * @param sGroup {string} the name of the group
15166      */
15167     addToGroup: function(sGroup) {
15168         this.groups[sGroup] = true;
15169         this.DDM.regDragDrop(this, sGroup);
15170     },
15171
15172     /**
15173      * Remove's this instance from the supplied interaction group
15174      * @method removeFromGroup
15175      * @param {string}  sGroup  The group to drop
15176      */
15177     removeFromGroup: function(sGroup) {
15178         if (this.groups[sGroup]) {
15179             delete this.groups[sGroup];
15180         }
15181
15182         this.DDM.removeDDFromGroup(this, sGroup);
15183     },
15184
15185     /**
15186      * Allows you to specify that an element other than the linked element
15187      * will be moved with the cursor during a drag
15188      * @method setDragElId
15189      * @param id {string} the id of the element that will be used to initiate the drag
15190      */
15191     setDragElId: function(id) {
15192         this.dragElId = id;
15193     },
15194
15195     /**
15196      * Allows you to specify a child of the linked element that should be
15197      * used to initiate the drag operation.  An example of this would be if
15198      * you have a content div with text and links.  Clicking anywhere in the
15199      * content area would normally start the drag operation.  Use this method
15200      * to specify that an element inside of the content div is the element
15201      * that starts the drag operation.
15202      * @method setHandleElId
15203      * @param id {string} the id of the element that will be used to
15204      * initiate the drag.
15205      */
15206     setHandleElId: function(id) {
15207         if (typeof id !== "string") {
15208             id = Roo.id(id);
15209         }
15210         this.handleElId = id;
15211         this.DDM.regHandle(this.id, id);
15212     },
15213
15214     /**
15215      * Allows you to set an element outside of the linked element as a drag
15216      * handle
15217      * @method setOuterHandleElId
15218      * @param id the id of the element that will be used to initiate the drag
15219      */
15220     setOuterHandleElId: function(id) {
15221         if (typeof id !== "string") {
15222             id = Roo.id(id);
15223         }
15224         Event.on(id, "mousedown",
15225                 this.handleMouseDown, this);
15226         this.setHandleElId(id);
15227
15228         this.hasOuterHandles = true;
15229     },
15230
15231     /**
15232      * Remove all drag and drop hooks for this element
15233      * @method unreg
15234      */
15235     unreg: function() {
15236         Event.un(this.id, "mousedown",
15237                 this.handleMouseDown);
15238         this._domRef = null;
15239         this.DDM._remove(this);
15240     },
15241
15242     destroy : function(){
15243         this.unreg();
15244     },
15245
15246     /**
15247      * Returns true if this instance is locked, or the drag drop mgr is locked
15248      * (meaning that all drag/drop is disabled on the page.)
15249      * @method isLocked
15250      * @return {boolean} true if this obj or all drag/drop is locked, else
15251      * false
15252      */
15253     isLocked: function() {
15254         return (this.DDM.isLocked() || this.locked);
15255     },
15256
15257     /**
15258      * Fired when this object is clicked
15259      * @method handleMouseDown
15260      * @param {Event} e
15261      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15262      * @private
15263      */
15264     handleMouseDown: function(e, oDD){
15265         if (this.primaryButtonOnly && e.button != 0) {
15266             return;
15267         }
15268
15269         if (this.isLocked()) {
15270             return;
15271         }
15272
15273         this.DDM.refreshCache(this.groups);
15274
15275         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15276         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15277         } else {
15278             if (this.clickValidator(e)) {
15279
15280                 // set the initial element position
15281                 this.setStartPosition();
15282
15283
15284                 this.b4MouseDown(e);
15285                 this.onMouseDown(e);
15286
15287                 this.DDM.handleMouseDown(e, this);
15288
15289                 this.DDM.stopEvent(e);
15290             } else {
15291
15292
15293             }
15294         }
15295     },
15296
15297     clickValidator: function(e) {
15298         var target = e.getTarget();
15299         return ( this.isValidHandleChild(target) &&
15300                     (this.id == this.handleElId ||
15301                         this.DDM.handleWasClicked(target, this.id)) );
15302     },
15303
15304     /**
15305      * Allows you to specify a tag name that should not start a drag operation
15306      * when clicked.  This is designed to facilitate embedding links within a
15307      * drag handle that do something other than start the drag.
15308      * @method addInvalidHandleType
15309      * @param {string} tagName the type of element to exclude
15310      */
15311     addInvalidHandleType: function(tagName) {
15312         var type = tagName.toUpperCase();
15313         this.invalidHandleTypes[type] = type;
15314     },
15315
15316     /**
15317      * Lets you to specify an element id for a child of a drag handle
15318      * that should not initiate a drag
15319      * @method addInvalidHandleId
15320      * @param {string} id the element id of the element you wish to ignore
15321      */
15322     addInvalidHandleId: function(id) {
15323         if (typeof id !== "string") {
15324             id = Roo.id(id);
15325         }
15326         this.invalidHandleIds[id] = id;
15327     },
15328
15329     /**
15330      * Lets you specify a css class of elements that will not initiate a drag
15331      * @method addInvalidHandleClass
15332      * @param {string} cssClass the class of the elements you wish to ignore
15333      */
15334     addInvalidHandleClass: function(cssClass) {
15335         this.invalidHandleClasses.push(cssClass);
15336     },
15337
15338     /**
15339      * Unsets an excluded tag name set by addInvalidHandleType
15340      * @method removeInvalidHandleType
15341      * @param {string} tagName the type of element to unexclude
15342      */
15343     removeInvalidHandleType: function(tagName) {
15344         var type = tagName.toUpperCase();
15345         // this.invalidHandleTypes[type] = null;
15346         delete this.invalidHandleTypes[type];
15347     },
15348
15349     /**
15350      * Unsets an invalid handle id
15351      * @method removeInvalidHandleId
15352      * @param {string} id the id of the element to re-enable
15353      */
15354     removeInvalidHandleId: function(id) {
15355         if (typeof id !== "string") {
15356             id = Roo.id(id);
15357         }
15358         delete this.invalidHandleIds[id];
15359     },
15360
15361     /**
15362      * Unsets an invalid css class
15363      * @method removeInvalidHandleClass
15364      * @param {string} cssClass the class of the element(s) you wish to
15365      * re-enable
15366      */
15367     removeInvalidHandleClass: function(cssClass) {
15368         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15369             if (this.invalidHandleClasses[i] == cssClass) {
15370                 delete this.invalidHandleClasses[i];
15371             }
15372         }
15373     },
15374
15375     /**
15376      * Checks the tag exclusion list to see if this click should be ignored
15377      * @method isValidHandleChild
15378      * @param {HTMLElement} node the HTMLElement to evaluate
15379      * @return {boolean} true if this is a valid tag type, false if not
15380      */
15381     isValidHandleChild: function(node) {
15382
15383         var valid = true;
15384         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15385         var nodeName;
15386         try {
15387             nodeName = node.nodeName.toUpperCase();
15388         } catch(e) {
15389             nodeName = node.nodeName;
15390         }
15391         valid = valid && !this.invalidHandleTypes[nodeName];
15392         valid = valid && !this.invalidHandleIds[node.id];
15393
15394         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15395             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15396         }
15397
15398
15399         return valid;
15400
15401     },
15402
15403     /**
15404      * Create the array of horizontal tick marks if an interval was specified
15405      * in setXConstraint().
15406      * @method setXTicks
15407      * @private
15408      */
15409     setXTicks: function(iStartX, iTickSize) {
15410         this.xTicks = [];
15411         this.xTickSize = iTickSize;
15412
15413         var tickMap = {};
15414
15415         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15416             if (!tickMap[i]) {
15417                 this.xTicks[this.xTicks.length] = i;
15418                 tickMap[i] = true;
15419             }
15420         }
15421
15422         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15423             if (!tickMap[i]) {
15424                 this.xTicks[this.xTicks.length] = i;
15425                 tickMap[i] = true;
15426             }
15427         }
15428
15429         this.xTicks.sort(this.DDM.numericSort) ;
15430     },
15431
15432     /**
15433      * Create the array of vertical tick marks if an interval was specified in
15434      * setYConstraint().
15435      * @method setYTicks
15436      * @private
15437      */
15438     setYTicks: function(iStartY, iTickSize) {
15439         this.yTicks = [];
15440         this.yTickSize = iTickSize;
15441
15442         var tickMap = {};
15443
15444         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15445             if (!tickMap[i]) {
15446                 this.yTicks[this.yTicks.length] = i;
15447                 tickMap[i] = true;
15448             }
15449         }
15450
15451         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15452             if (!tickMap[i]) {
15453                 this.yTicks[this.yTicks.length] = i;
15454                 tickMap[i] = true;
15455             }
15456         }
15457
15458         this.yTicks.sort(this.DDM.numericSort) ;
15459     },
15460
15461     /**
15462      * By default, the element can be dragged any place on the screen.  Use
15463      * this method to limit the horizontal travel of the element.  Pass in
15464      * 0,0 for the parameters if you want to lock the drag to the y axis.
15465      * @method setXConstraint
15466      * @param {int} iLeft the number of pixels the element can move to the left
15467      * @param {int} iRight the number of pixels the element can move to the
15468      * right
15469      * @param {int} iTickSize optional parameter for specifying that the
15470      * element
15471      * should move iTickSize pixels at a time.
15472      */
15473     setXConstraint: function(iLeft, iRight, iTickSize) {
15474         this.leftConstraint = iLeft;
15475         this.rightConstraint = iRight;
15476
15477         this.minX = this.initPageX - iLeft;
15478         this.maxX = this.initPageX + iRight;
15479         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15480
15481         this.constrainX = true;
15482     },
15483
15484     /**
15485      * Clears any constraints applied to this instance.  Also clears ticks
15486      * since they can't exist independent of a constraint at this time.
15487      * @method clearConstraints
15488      */
15489     clearConstraints: function() {
15490         this.constrainX = false;
15491         this.constrainY = false;
15492         this.clearTicks();
15493     },
15494
15495     /**
15496      * Clears any tick interval defined for this instance
15497      * @method clearTicks
15498      */
15499     clearTicks: function() {
15500         this.xTicks = null;
15501         this.yTicks = null;
15502         this.xTickSize = 0;
15503         this.yTickSize = 0;
15504     },
15505
15506     /**
15507      * By default, the element can be dragged any place on the screen.  Set
15508      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15509      * parameters if you want to lock the drag to the x axis.
15510      * @method setYConstraint
15511      * @param {int} iUp the number of pixels the element can move up
15512      * @param {int} iDown the number of pixels the element can move down
15513      * @param {int} iTickSize optional parameter for specifying that the
15514      * element should move iTickSize pixels at a time.
15515      */
15516     setYConstraint: function(iUp, iDown, iTickSize) {
15517         this.topConstraint = iUp;
15518         this.bottomConstraint = iDown;
15519
15520         this.minY = this.initPageY - iUp;
15521         this.maxY = this.initPageY + iDown;
15522         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15523
15524         this.constrainY = true;
15525
15526     },
15527
15528     /**
15529      * resetConstraints must be called if you manually reposition a dd element.
15530      * @method resetConstraints
15531      * @param {boolean} maintainOffset
15532      */
15533     resetConstraints: function() {
15534
15535
15536         // Maintain offsets if necessary
15537         if (this.initPageX || this.initPageX === 0) {
15538             // figure out how much this thing has moved
15539             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15540             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15541
15542             this.setInitPosition(dx, dy);
15543
15544         // This is the first time we have detected the element's position
15545         } else {
15546             this.setInitPosition();
15547         }
15548
15549         if (this.constrainX) {
15550             this.setXConstraint( this.leftConstraint,
15551                                  this.rightConstraint,
15552                                  this.xTickSize        );
15553         }
15554
15555         if (this.constrainY) {
15556             this.setYConstraint( this.topConstraint,
15557                                  this.bottomConstraint,
15558                                  this.yTickSize         );
15559         }
15560     },
15561
15562     /**
15563      * Normally the drag element is moved pixel by pixel, but we can specify
15564      * that it move a number of pixels at a time.  This method resolves the
15565      * location when we have it set up like this.
15566      * @method getTick
15567      * @param {int} val where we want to place the object
15568      * @param {int[]} tickArray sorted array of valid points
15569      * @return {int} the closest tick
15570      * @private
15571      */
15572     getTick: function(val, tickArray) {
15573
15574         if (!tickArray) {
15575             // If tick interval is not defined, it is effectively 1 pixel,
15576             // so we return the value passed to us.
15577             return val;
15578         } else if (tickArray[0] >= val) {
15579             // The value is lower than the first tick, so we return the first
15580             // tick.
15581             return tickArray[0];
15582         } else {
15583             for (var i=0, len=tickArray.length; i<len; ++i) {
15584                 var next = i + 1;
15585                 if (tickArray[next] && tickArray[next] >= val) {
15586                     var diff1 = val - tickArray[i];
15587                     var diff2 = tickArray[next] - val;
15588                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15589                 }
15590             }
15591
15592             // The value is larger than the last tick, so we return the last
15593             // tick.
15594             return tickArray[tickArray.length - 1];
15595         }
15596     },
15597
15598     /**
15599      * toString method
15600      * @method toString
15601      * @return {string} string representation of the dd obj
15602      */
15603     toString: function() {
15604         return ("DragDrop " + this.id);
15605     }
15606
15607 });
15608
15609 })();
15610 /*
15611  * Based on:
15612  * Ext JS Library 1.1.1
15613  * Copyright(c) 2006-2007, Ext JS, LLC.
15614  *
15615  * Originally Released Under LGPL - original licence link has changed is not relivant.
15616  *
15617  * Fork - LGPL
15618  * <script type="text/javascript">
15619  */
15620
15621
15622 /**
15623  * The drag and drop utility provides a framework for building drag and drop
15624  * applications.  In addition to enabling drag and drop for specific elements,
15625  * the drag and drop elements are tracked by the manager class, and the
15626  * interactions between the various elements are tracked during the drag and
15627  * the implementing code is notified about these important moments.
15628  */
15629
15630 // Only load the library once.  Rewriting the manager class would orphan
15631 // existing drag and drop instances.
15632 if (!Roo.dd.DragDropMgr) {
15633
15634 /**
15635  * @class Roo.dd.DragDropMgr
15636  * DragDropMgr is a singleton that tracks the element interaction for
15637  * all DragDrop items in the window.  Generally, you will not call
15638  * this class directly, but it does have helper methods that could
15639  * be useful in your DragDrop implementations.
15640  * @singleton
15641  */
15642 Roo.dd.DragDropMgr = function() {
15643
15644     var Event = Roo.EventManager;
15645
15646     return {
15647
15648         /**
15649          * Two dimensional Array of registered DragDrop objects.  The first
15650          * dimension is the DragDrop item group, the second the DragDrop
15651          * object.
15652          * @property ids
15653          * @type {string: string}
15654          * @private
15655          * @static
15656          */
15657         ids: {},
15658
15659         /**
15660          * Array of element ids defined as drag handles.  Used to determine
15661          * if the element that generated the mousedown event is actually the
15662          * handle and not the html element itself.
15663          * @property handleIds
15664          * @type {string: string}
15665          * @private
15666          * @static
15667          */
15668         handleIds: {},
15669
15670         /**
15671          * the DragDrop object that is currently being dragged
15672          * @property dragCurrent
15673          * @type DragDrop
15674          * @private
15675          * @static
15676          **/
15677         dragCurrent: null,
15678
15679         /**
15680          * the DragDrop object(s) that are being hovered over
15681          * @property dragOvers
15682          * @type Array
15683          * @private
15684          * @static
15685          */
15686         dragOvers: {},
15687
15688         /**
15689          * the X distance between the cursor and the object being dragged
15690          * @property deltaX
15691          * @type int
15692          * @private
15693          * @static
15694          */
15695         deltaX: 0,
15696
15697         /**
15698          * the Y distance between the cursor and the object being dragged
15699          * @property deltaY
15700          * @type int
15701          * @private
15702          * @static
15703          */
15704         deltaY: 0,
15705
15706         /**
15707          * Flag to determine if we should prevent the default behavior of the
15708          * events we define. By default this is true, but this can be set to
15709          * false if you need the default behavior (not recommended)
15710          * @property preventDefault
15711          * @type boolean
15712          * @static
15713          */
15714         preventDefault: true,
15715
15716         /**
15717          * Flag to determine if we should stop the propagation of the events
15718          * we generate. This is true by default but you may want to set it to
15719          * false if the html element contains other features that require the
15720          * mouse click.
15721          * @property stopPropagation
15722          * @type boolean
15723          * @static
15724          */
15725         stopPropagation: true,
15726
15727         /**
15728          * Internal flag that is set to true when drag and drop has been
15729          * intialized
15730          * @property initialized
15731          * @private
15732          * @static
15733          */
15734         initalized: false,
15735
15736         /**
15737          * All drag and drop can be disabled.
15738          * @property locked
15739          * @private
15740          * @static
15741          */
15742         locked: false,
15743
15744         /**
15745          * Called the first time an element is registered.
15746          * @method init
15747          * @private
15748          * @static
15749          */
15750         init: function() {
15751             this.initialized = true;
15752         },
15753
15754         /**
15755          * In point mode, drag and drop interaction is defined by the
15756          * location of the cursor during the drag/drop
15757          * @property POINT
15758          * @type int
15759          * @static
15760          */
15761         POINT: 0,
15762
15763         /**
15764          * In intersect mode, drag and drop interactio nis defined by the
15765          * overlap of two or more drag and drop objects.
15766          * @property INTERSECT
15767          * @type int
15768          * @static
15769          */
15770         INTERSECT: 1,
15771
15772         /**
15773          * The current drag and drop mode.  Default: POINT
15774          * @property mode
15775          * @type int
15776          * @static
15777          */
15778         mode: 0,
15779
15780         /**
15781          * Runs method on all drag and drop objects
15782          * @method _execOnAll
15783          * @private
15784          * @static
15785          */
15786         _execOnAll: function(sMethod, args) {
15787             for (var i in this.ids) {
15788                 for (var j in this.ids[i]) {
15789                     var oDD = this.ids[i][j];
15790                     if (! this.isTypeOfDD(oDD)) {
15791                         continue;
15792                     }
15793                     oDD[sMethod].apply(oDD, args);
15794                 }
15795             }
15796         },
15797
15798         /**
15799          * Drag and drop initialization.  Sets up the global event handlers
15800          * @method _onLoad
15801          * @private
15802          * @static
15803          */
15804         _onLoad: function() {
15805
15806             this.init();
15807
15808
15809             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15810             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15811             Event.on(window,   "unload",    this._onUnload, this, true);
15812             Event.on(window,   "resize",    this._onResize, this, true);
15813             // Event.on(window,   "mouseout",    this._test);
15814
15815         },
15816
15817         /**
15818          * Reset constraints on all drag and drop objs
15819          * @method _onResize
15820          * @private
15821          * @static
15822          */
15823         _onResize: function(e) {
15824             this._execOnAll("resetConstraints", []);
15825         },
15826
15827         /**
15828          * Lock all drag and drop functionality
15829          * @method lock
15830          * @static
15831          */
15832         lock: function() { this.locked = true; },
15833
15834         /**
15835          * Unlock all drag and drop functionality
15836          * @method unlock
15837          * @static
15838          */
15839         unlock: function() { this.locked = false; },
15840
15841         /**
15842          * Is drag and drop locked?
15843          * @method isLocked
15844          * @return {boolean} True if drag and drop is locked, false otherwise.
15845          * @static
15846          */
15847         isLocked: function() { return this.locked; },
15848
15849         /**
15850          * Location cache that is set for all drag drop objects when a drag is
15851          * initiated, cleared when the drag is finished.
15852          * @property locationCache
15853          * @private
15854          * @static
15855          */
15856         locationCache: {},
15857
15858         /**
15859          * Set useCache to false if you want to force object the lookup of each
15860          * drag and drop linked element constantly during a drag.
15861          * @property useCache
15862          * @type boolean
15863          * @static
15864          */
15865         useCache: true,
15866
15867         /**
15868          * The number of pixels that the mouse needs to move after the
15869          * mousedown before the drag is initiated.  Default=3;
15870          * @property clickPixelThresh
15871          * @type int
15872          * @static
15873          */
15874         clickPixelThresh: 3,
15875
15876         /**
15877          * The number of milliseconds after the mousedown event to initiate the
15878          * drag if we don't get a mouseup event. Default=1000
15879          * @property clickTimeThresh
15880          * @type int
15881          * @static
15882          */
15883         clickTimeThresh: 350,
15884
15885         /**
15886          * Flag that indicates that either the drag pixel threshold or the
15887          * mousdown time threshold has been met
15888          * @property dragThreshMet
15889          * @type boolean
15890          * @private
15891          * @static
15892          */
15893         dragThreshMet: false,
15894
15895         /**
15896          * Timeout used for the click time threshold
15897          * @property clickTimeout
15898          * @type Object
15899          * @private
15900          * @static
15901          */
15902         clickTimeout: null,
15903
15904         /**
15905          * The X position of the mousedown event stored for later use when a
15906          * drag threshold is met.
15907          * @property startX
15908          * @type int
15909          * @private
15910          * @static
15911          */
15912         startX: 0,
15913
15914         /**
15915          * The Y position of the mousedown event stored for later use when a
15916          * drag threshold is met.
15917          * @property startY
15918          * @type int
15919          * @private
15920          * @static
15921          */
15922         startY: 0,
15923
15924         /**
15925          * Each DragDrop instance must be registered with the DragDropMgr.
15926          * This is executed in DragDrop.init()
15927          * @method regDragDrop
15928          * @param {DragDrop} oDD the DragDrop object to register
15929          * @param {String} sGroup the name of the group this element belongs to
15930          * @static
15931          */
15932         regDragDrop: function(oDD, sGroup) {
15933             if (!this.initialized) { this.init(); }
15934
15935             if (!this.ids[sGroup]) {
15936                 this.ids[sGroup] = {};
15937             }
15938             this.ids[sGroup][oDD.id] = oDD;
15939         },
15940
15941         /**
15942          * Removes the supplied dd instance from the supplied group. Executed
15943          * by DragDrop.removeFromGroup, so don't call this function directly.
15944          * @method removeDDFromGroup
15945          * @private
15946          * @static
15947          */
15948         removeDDFromGroup: function(oDD, sGroup) {
15949             if (!this.ids[sGroup]) {
15950                 this.ids[sGroup] = {};
15951             }
15952
15953             var obj = this.ids[sGroup];
15954             if (obj && obj[oDD.id]) {
15955                 delete obj[oDD.id];
15956             }
15957         },
15958
15959         /**
15960          * Unregisters a drag and drop item.  This is executed in
15961          * DragDrop.unreg, use that method instead of calling this directly.
15962          * @method _remove
15963          * @private
15964          * @static
15965          */
15966         _remove: function(oDD) {
15967             for (var g in oDD.groups) {
15968                 if (g && this.ids[g][oDD.id]) {
15969                     delete this.ids[g][oDD.id];
15970                 }
15971             }
15972             delete this.handleIds[oDD.id];
15973         },
15974
15975         /**
15976          * Each DragDrop handle element must be registered.  This is done
15977          * automatically when executing DragDrop.setHandleElId()
15978          * @method regHandle
15979          * @param {String} sDDId the DragDrop id this element is a handle for
15980          * @param {String} sHandleId the id of the element that is the drag
15981          * handle
15982          * @static
15983          */
15984         regHandle: function(sDDId, sHandleId) {
15985             if (!this.handleIds[sDDId]) {
15986                 this.handleIds[sDDId] = {};
15987             }
15988             this.handleIds[sDDId][sHandleId] = sHandleId;
15989         },
15990
15991         /**
15992          * Utility function to determine if a given element has been
15993          * registered as a drag drop item.
15994          * @method isDragDrop
15995          * @param {String} id the element id to check
15996          * @return {boolean} true if this element is a DragDrop item,
15997          * false otherwise
15998          * @static
15999          */
16000         isDragDrop: function(id) {
16001             return ( this.getDDById(id) ) ? true : false;
16002         },
16003
16004         /**
16005          * Returns the drag and drop instances that are in all groups the
16006          * passed in instance belongs to.
16007          * @method getRelated
16008          * @param {DragDrop} p_oDD the obj to get related data for
16009          * @param {boolean} bTargetsOnly if true, only return targetable objs
16010          * @return {DragDrop[]} the related instances
16011          * @static
16012          */
16013         getRelated: function(p_oDD, bTargetsOnly) {
16014             var oDDs = [];
16015             for (var i in p_oDD.groups) {
16016                 for (j in this.ids[i]) {
16017                     var dd = this.ids[i][j];
16018                     if (! this.isTypeOfDD(dd)) {
16019                         continue;
16020                     }
16021                     if (!bTargetsOnly || dd.isTarget) {
16022                         oDDs[oDDs.length] = dd;
16023                     }
16024                 }
16025             }
16026
16027             return oDDs;
16028         },
16029
16030         /**
16031          * Returns true if the specified dd target is a legal target for
16032          * the specifice drag obj
16033          * @method isLegalTarget
16034          * @param {DragDrop} the drag obj
16035          * @param {DragDrop} the target
16036          * @return {boolean} true if the target is a legal target for the
16037          * dd obj
16038          * @static
16039          */
16040         isLegalTarget: function (oDD, oTargetDD) {
16041             var targets = this.getRelated(oDD, true);
16042             for (var i=0, len=targets.length;i<len;++i) {
16043                 if (targets[i].id == oTargetDD.id) {
16044                     return true;
16045                 }
16046             }
16047
16048             return false;
16049         },
16050
16051         /**
16052          * My goal is to be able to transparently determine if an object is
16053          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16054          * returns "object", oDD.constructor.toString() always returns
16055          * "DragDrop" and not the name of the subclass.  So for now it just
16056          * evaluates a well-known variable in DragDrop.
16057          * @method isTypeOfDD
16058          * @param {Object} the object to evaluate
16059          * @return {boolean} true if typeof oDD = DragDrop
16060          * @static
16061          */
16062         isTypeOfDD: function (oDD) {
16063             return (oDD && oDD.__ygDragDrop);
16064         },
16065
16066         /**
16067          * Utility function to determine if a given element has been
16068          * registered as a drag drop handle for the given Drag Drop object.
16069          * @method isHandle
16070          * @param {String} id the element id to check
16071          * @return {boolean} true if this element is a DragDrop handle, false
16072          * otherwise
16073          * @static
16074          */
16075         isHandle: function(sDDId, sHandleId) {
16076             return ( this.handleIds[sDDId] &&
16077                             this.handleIds[sDDId][sHandleId] );
16078         },
16079
16080         /**
16081          * Returns the DragDrop instance for a given id
16082          * @method getDDById
16083          * @param {String} id the id of the DragDrop object
16084          * @return {DragDrop} the drag drop object, null if it is not found
16085          * @static
16086          */
16087         getDDById: function(id) {
16088             for (var i in this.ids) {
16089                 if (this.ids[i][id]) {
16090                     return this.ids[i][id];
16091                 }
16092             }
16093             return null;
16094         },
16095
16096         /**
16097          * Fired after a registered DragDrop object gets the mousedown event.
16098          * Sets up the events required to track the object being dragged
16099          * @method handleMouseDown
16100          * @param {Event} e the event
16101          * @param oDD the DragDrop object being dragged
16102          * @private
16103          * @static
16104          */
16105         handleMouseDown: function(e, oDD) {
16106             if(Roo.QuickTips){
16107                 Roo.QuickTips.disable();
16108             }
16109             this.currentTarget = e.getTarget();
16110
16111             this.dragCurrent = oDD;
16112
16113             var el = oDD.getEl();
16114
16115             // track start position
16116             this.startX = e.getPageX();
16117             this.startY = e.getPageY();
16118
16119             this.deltaX = this.startX - el.offsetLeft;
16120             this.deltaY = this.startY - el.offsetTop;
16121
16122             this.dragThreshMet = false;
16123
16124             this.clickTimeout = setTimeout(
16125                     function() {
16126                         var DDM = Roo.dd.DDM;
16127                         DDM.startDrag(DDM.startX, DDM.startY);
16128                     },
16129                     this.clickTimeThresh );
16130         },
16131
16132         /**
16133          * Fired when either the drag pixel threshol or the mousedown hold
16134          * time threshold has been met.
16135          * @method startDrag
16136          * @param x {int} the X position of the original mousedown
16137          * @param y {int} the Y position of the original mousedown
16138          * @static
16139          */
16140         startDrag: function(x, y) {
16141             clearTimeout(this.clickTimeout);
16142             if (this.dragCurrent) {
16143                 this.dragCurrent.b4StartDrag(x, y);
16144                 this.dragCurrent.startDrag(x, y);
16145             }
16146             this.dragThreshMet = true;
16147         },
16148
16149         /**
16150          * Internal function to handle the mouseup event.  Will be invoked
16151          * from the context of the document.
16152          * @method handleMouseUp
16153          * @param {Event} e the event
16154          * @private
16155          * @static
16156          */
16157         handleMouseUp: function(e) {
16158
16159             if(Roo.QuickTips){
16160                 Roo.QuickTips.enable();
16161             }
16162             if (! this.dragCurrent) {
16163                 return;
16164             }
16165
16166             clearTimeout(this.clickTimeout);
16167
16168             if (this.dragThreshMet) {
16169                 this.fireEvents(e, true);
16170             } else {
16171             }
16172
16173             this.stopDrag(e);
16174
16175             this.stopEvent(e);
16176         },
16177
16178         /**
16179          * Utility to stop event propagation and event default, if these
16180          * features are turned on.
16181          * @method stopEvent
16182          * @param {Event} e the event as returned by this.getEvent()
16183          * @static
16184          */
16185         stopEvent: function(e){
16186             if(this.stopPropagation) {
16187                 e.stopPropagation();
16188             }
16189
16190             if (this.preventDefault) {
16191                 e.preventDefault();
16192             }
16193         },
16194
16195         /**
16196          * Internal function to clean up event handlers after the drag
16197          * operation is complete
16198          * @method stopDrag
16199          * @param {Event} e the event
16200          * @private
16201          * @static
16202          */
16203         stopDrag: function(e) {
16204             // Fire the drag end event for the item that was dragged
16205             if (this.dragCurrent) {
16206                 if (this.dragThreshMet) {
16207                     this.dragCurrent.b4EndDrag(e);
16208                     this.dragCurrent.endDrag(e);
16209                 }
16210
16211                 this.dragCurrent.onMouseUp(e);
16212             }
16213
16214             this.dragCurrent = null;
16215             this.dragOvers = {};
16216         },
16217
16218         /**
16219          * Internal function to handle the mousemove event.  Will be invoked
16220          * from the context of the html element.
16221          *
16222          * @TODO figure out what we can do about mouse events lost when the
16223          * user drags objects beyond the window boundary.  Currently we can
16224          * detect this in internet explorer by verifying that the mouse is
16225          * down during the mousemove event.  Firefox doesn't give us the
16226          * button state on the mousemove event.
16227          * @method handleMouseMove
16228          * @param {Event} e the event
16229          * @private
16230          * @static
16231          */
16232         handleMouseMove: function(e) {
16233             if (! this.dragCurrent) {
16234                 return true;
16235             }
16236
16237             // var button = e.which || e.button;
16238
16239             // check for IE mouseup outside of page boundary
16240             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16241                 this.stopEvent(e);
16242                 return this.handleMouseUp(e);
16243             }
16244
16245             if (!this.dragThreshMet) {
16246                 var diffX = Math.abs(this.startX - e.getPageX());
16247                 var diffY = Math.abs(this.startY - e.getPageY());
16248                 if (diffX > this.clickPixelThresh ||
16249                             diffY > this.clickPixelThresh) {
16250                     this.startDrag(this.startX, this.startY);
16251                 }
16252             }
16253
16254             if (this.dragThreshMet) {
16255                 this.dragCurrent.b4Drag(e);
16256                 this.dragCurrent.onDrag(e);
16257                 if(!this.dragCurrent.moveOnly){
16258                     this.fireEvents(e, false);
16259                 }
16260             }
16261
16262             this.stopEvent(e);
16263
16264             return true;
16265         },
16266
16267         /**
16268          * Iterates over all of the DragDrop elements to find ones we are
16269          * hovering over or dropping on
16270          * @method fireEvents
16271          * @param {Event} e the event
16272          * @param {boolean} isDrop is this a drop op or a mouseover op?
16273          * @private
16274          * @static
16275          */
16276         fireEvents: function(e, isDrop) {
16277             var dc = this.dragCurrent;
16278
16279             // If the user did the mouse up outside of the window, we could
16280             // get here even though we have ended the drag.
16281             if (!dc || dc.isLocked()) {
16282                 return;
16283             }
16284
16285             var pt = e.getPoint();
16286
16287             // cache the previous dragOver array
16288             var oldOvers = [];
16289
16290             var outEvts   = [];
16291             var overEvts  = [];
16292             var dropEvts  = [];
16293             var enterEvts = [];
16294
16295             // Check to see if the object(s) we were hovering over is no longer
16296             // being hovered over so we can fire the onDragOut event
16297             for (var i in this.dragOvers) {
16298
16299                 var ddo = this.dragOvers[i];
16300
16301                 if (! this.isTypeOfDD(ddo)) {
16302                     continue;
16303                 }
16304
16305                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16306                     outEvts.push( ddo );
16307                 }
16308
16309                 oldOvers[i] = true;
16310                 delete this.dragOvers[i];
16311             }
16312
16313             for (var sGroup in dc.groups) {
16314
16315                 if ("string" != typeof sGroup) {
16316                     continue;
16317                 }
16318
16319                 for (i in this.ids[sGroup]) {
16320                     var oDD = this.ids[sGroup][i];
16321                     if (! this.isTypeOfDD(oDD)) {
16322                         continue;
16323                     }
16324
16325                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16326                         if (this.isOverTarget(pt, oDD, this.mode)) {
16327                             // look for drop interactions
16328                             if (isDrop) {
16329                                 dropEvts.push( oDD );
16330                             // look for drag enter and drag over interactions
16331                             } else {
16332
16333                                 // initial drag over: dragEnter fires
16334                                 if (!oldOvers[oDD.id]) {
16335                                     enterEvts.push( oDD );
16336                                 // subsequent drag overs: dragOver fires
16337                                 } else {
16338                                     overEvts.push( oDD );
16339                                 }
16340
16341                                 this.dragOvers[oDD.id] = oDD;
16342                             }
16343                         }
16344                     }
16345                 }
16346             }
16347
16348             if (this.mode) {
16349                 if (outEvts.length) {
16350                     dc.b4DragOut(e, outEvts);
16351                     dc.onDragOut(e, outEvts);
16352                 }
16353
16354                 if (enterEvts.length) {
16355                     dc.onDragEnter(e, enterEvts);
16356                 }
16357
16358                 if (overEvts.length) {
16359                     dc.b4DragOver(e, overEvts);
16360                     dc.onDragOver(e, overEvts);
16361                 }
16362
16363                 if (dropEvts.length) {
16364                     dc.b4DragDrop(e, dropEvts);
16365                     dc.onDragDrop(e, dropEvts);
16366                 }
16367
16368             } else {
16369                 // fire dragout events
16370                 var len = 0;
16371                 for (i=0, len=outEvts.length; i<len; ++i) {
16372                     dc.b4DragOut(e, outEvts[i].id);
16373                     dc.onDragOut(e, outEvts[i].id);
16374                 }
16375
16376                 // fire enter events
16377                 for (i=0,len=enterEvts.length; i<len; ++i) {
16378                     // dc.b4DragEnter(e, oDD.id);
16379                     dc.onDragEnter(e, enterEvts[i].id);
16380                 }
16381
16382                 // fire over events
16383                 for (i=0,len=overEvts.length; i<len; ++i) {
16384                     dc.b4DragOver(e, overEvts[i].id);
16385                     dc.onDragOver(e, overEvts[i].id);
16386                 }
16387
16388                 // fire drop events
16389                 for (i=0, len=dropEvts.length; i<len; ++i) {
16390                     dc.b4DragDrop(e, dropEvts[i].id);
16391                     dc.onDragDrop(e, dropEvts[i].id);
16392                 }
16393
16394             }
16395
16396             // notify about a drop that did not find a target
16397             if (isDrop && !dropEvts.length) {
16398                 dc.onInvalidDrop(e);
16399             }
16400
16401         },
16402
16403         /**
16404          * Helper function for getting the best match from the list of drag
16405          * and drop objects returned by the drag and drop events when we are
16406          * in INTERSECT mode.  It returns either the first object that the
16407          * cursor is over, or the object that has the greatest overlap with
16408          * the dragged element.
16409          * @method getBestMatch
16410          * @param  {DragDrop[]} dds The array of drag and drop objects
16411          * targeted
16412          * @return {DragDrop}       The best single match
16413          * @static
16414          */
16415         getBestMatch: function(dds) {
16416             var winner = null;
16417             // Return null if the input is not what we expect
16418             //if (!dds || !dds.length || dds.length == 0) {
16419                // winner = null;
16420             // If there is only one item, it wins
16421             //} else if (dds.length == 1) {
16422
16423             var len = dds.length;
16424
16425             if (len == 1) {
16426                 winner = dds[0];
16427             } else {
16428                 // Loop through the targeted items
16429                 for (var i=0; i<len; ++i) {
16430                     var dd = dds[i];
16431                     // If the cursor is over the object, it wins.  If the
16432                     // cursor is over multiple matches, the first one we come
16433                     // to wins.
16434                     if (dd.cursorIsOver) {
16435                         winner = dd;
16436                         break;
16437                     // Otherwise the object with the most overlap wins
16438                     } else {
16439                         if (!winner ||
16440                             winner.overlap.getArea() < dd.overlap.getArea()) {
16441                             winner = dd;
16442                         }
16443                     }
16444                 }
16445             }
16446
16447             return winner;
16448         },
16449
16450         /**
16451          * Refreshes the cache of the top-left and bottom-right points of the
16452          * drag and drop objects in the specified group(s).  This is in the
16453          * format that is stored in the drag and drop instance, so typical
16454          * usage is:
16455          * <code>
16456          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16457          * </code>
16458          * Alternatively:
16459          * <code>
16460          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16461          * </code>
16462          * @TODO this really should be an indexed array.  Alternatively this
16463          * method could accept both.
16464          * @method refreshCache
16465          * @param {Object} groups an associative array of groups to refresh
16466          * @static
16467          */
16468         refreshCache: function(groups) {
16469             for (var sGroup in groups) {
16470                 if ("string" != typeof sGroup) {
16471                     continue;
16472                 }
16473                 for (var i in this.ids[sGroup]) {
16474                     var oDD = this.ids[sGroup][i];
16475
16476                     if (this.isTypeOfDD(oDD)) {
16477                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16478                         var loc = this.getLocation(oDD);
16479                         if (loc) {
16480                             this.locationCache[oDD.id] = loc;
16481                         } else {
16482                             delete this.locationCache[oDD.id];
16483                             // this will unregister the drag and drop object if
16484                             // the element is not in a usable state
16485                             // oDD.unreg();
16486                         }
16487                     }
16488                 }
16489             }
16490         },
16491
16492         /**
16493          * This checks to make sure an element exists and is in the DOM.  The
16494          * main purpose is to handle cases where innerHTML is used to remove
16495          * drag and drop objects from the DOM.  IE provides an 'unspecified
16496          * error' when trying to access the offsetParent of such an element
16497          * @method verifyEl
16498          * @param {HTMLElement} el the element to check
16499          * @return {boolean} true if the element looks usable
16500          * @static
16501          */
16502         verifyEl: function(el) {
16503             if (el) {
16504                 var parent;
16505                 if(Roo.isIE){
16506                     try{
16507                         parent = el.offsetParent;
16508                     }catch(e){}
16509                 }else{
16510                     parent = el.offsetParent;
16511                 }
16512                 if (parent) {
16513                     return true;
16514                 }
16515             }
16516
16517             return false;
16518         },
16519
16520         /**
16521          * Returns a Region object containing the drag and drop element's position
16522          * and size, including the padding configured for it
16523          * @method getLocation
16524          * @param {DragDrop} oDD the drag and drop object to get the
16525          *                       location for
16526          * @return {Roo.lib.Region} a Region object representing the total area
16527          *                             the element occupies, including any padding
16528          *                             the instance is configured for.
16529          * @static
16530          */
16531         getLocation: function(oDD) {
16532             if (! this.isTypeOfDD(oDD)) {
16533                 return null;
16534             }
16535
16536             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16537
16538             try {
16539                 pos= Roo.lib.Dom.getXY(el);
16540             } catch (e) { }
16541
16542             if (!pos) {
16543                 return null;
16544             }
16545
16546             x1 = pos[0];
16547             x2 = x1 + el.offsetWidth;
16548             y1 = pos[1];
16549             y2 = y1 + el.offsetHeight;
16550
16551             t = y1 - oDD.padding[0];
16552             r = x2 + oDD.padding[1];
16553             b = y2 + oDD.padding[2];
16554             l = x1 - oDD.padding[3];
16555
16556             return new Roo.lib.Region( t, r, b, l );
16557         },
16558
16559         /**
16560          * Checks the cursor location to see if it over the target
16561          * @method isOverTarget
16562          * @param {Roo.lib.Point} pt The point to evaluate
16563          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16564          * @return {boolean} true if the mouse is over the target
16565          * @private
16566          * @static
16567          */
16568         isOverTarget: function(pt, oTarget, intersect) {
16569             // use cache if available
16570             var loc = this.locationCache[oTarget.id];
16571             if (!loc || !this.useCache) {
16572                 loc = this.getLocation(oTarget);
16573                 this.locationCache[oTarget.id] = loc;
16574
16575             }
16576
16577             if (!loc) {
16578                 return false;
16579             }
16580
16581             oTarget.cursorIsOver = loc.contains( pt );
16582
16583             // DragDrop is using this as a sanity check for the initial mousedown
16584             // in this case we are done.  In POINT mode, if the drag obj has no
16585             // contraints, we are also done. Otherwise we need to evaluate the
16586             // location of the target as related to the actual location of the
16587             // dragged element.
16588             var dc = this.dragCurrent;
16589             if (!dc || !dc.getTargetCoord ||
16590                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16591                 return oTarget.cursorIsOver;
16592             }
16593
16594             oTarget.overlap = null;
16595
16596             // Get the current location of the drag element, this is the
16597             // location of the mouse event less the delta that represents
16598             // where the original mousedown happened on the element.  We
16599             // need to consider constraints and ticks as well.
16600             var pos = dc.getTargetCoord(pt.x, pt.y);
16601
16602             var el = dc.getDragEl();
16603             var curRegion = new Roo.lib.Region( pos.y,
16604                                                    pos.x + el.offsetWidth,
16605                                                    pos.y + el.offsetHeight,
16606                                                    pos.x );
16607
16608             var overlap = curRegion.intersect(loc);
16609
16610             if (overlap) {
16611                 oTarget.overlap = overlap;
16612                 return (intersect) ? true : oTarget.cursorIsOver;
16613             } else {
16614                 return false;
16615             }
16616         },
16617
16618         /**
16619          * unload event handler
16620          * @method _onUnload
16621          * @private
16622          * @static
16623          */
16624         _onUnload: function(e, me) {
16625             Roo.dd.DragDropMgr.unregAll();
16626         },
16627
16628         /**
16629          * Cleans up the drag and drop events and objects.
16630          * @method unregAll
16631          * @private
16632          * @static
16633          */
16634         unregAll: function() {
16635
16636             if (this.dragCurrent) {
16637                 this.stopDrag();
16638                 this.dragCurrent = null;
16639             }
16640
16641             this._execOnAll("unreg", []);
16642
16643             for (i in this.elementCache) {
16644                 delete this.elementCache[i];
16645             }
16646
16647             this.elementCache = {};
16648             this.ids = {};
16649         },
16650
16651         /**
16652          * A cache of DOM elements
16653          * @property elementCache
16654          * @private
16655          * @static
16656          */
16657         elementCache: {},
16658
16659         /**
16660          * Get the wrapper for the DOM element specified
16661          * @method getElWrapper
16662          * @param {String} id the id of the element to get
16663          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16664          * @private
16665          * @deprecated This wrapper isn't that useful
16666          * @static
16667          */
16668         getElWrapper: function(id) {
16669             var oWrapper = this.elementCache[id];
16670             if (!oWrapper || !oWrapper.el) {
16671                 oWrapper = this.elementCache[id] =
16672                     new this.ElementWrapper(Roo.getDom(id));
16673             }
16674             return oWrapper;
16675         },
16676
16677         /**
16678          * Returns the actual DOM element
16679          * @method getElement
16680          * @param {String} id the id of the elment to get
16681          * @return {Object} The element
16682          * @deprecated use Roo.getDom instead
16683          * @static
16684          */
16685         getElement: function(id) {
16686             return Roo.getDom(id);
16687         },
16688
16689         /**
16690          * Returns the style property for the DOM element (i.e.,
16691          * document.getElById(id).style)
16692          * @method getCss
16693          * @param {String} id the id of the elment to get
16694          * @return {Object} The style property of the element
16695          * @deprecated use Roo.getDom instead
16696          * @static
16697          */
16698         getCss: function(id) {
16699             var el = Roo.getDom(id);
16700             return (el) ? el.style : null;
16701         },
16702
16703         /**
16704          * Inner class for cached elements
16705          * @class DragDropMgr.ElementWrapper
16706          * @for DragDropMgr
16707          * @private
16708          * @deprecated
16709          */
16710         ElementWrapper: function(el) {
16711                 /**
16712                  * The element
16713                  * @property el
16714                  */
16715                 this.el = el || null;
16716                 /**
16717                  * The element id
16718                  * @property id
16719                  */
16720                 this.id = this.el && el.id;
16721                 /**
16722                  * A reference to the style property
16723                  * @property css
16724                  */
16725                 this.css = this.el && el.style;
16726             },
16727
16728         /**
16729          * Returns the X position of an html element
16730          * @method getPosX
16731          * @param el the element for which to get the position
16732          * @return {int} the X coordinate
16733          * @for DragDropMgr
16734          * @deprecated use Roo.lib.Dom.getX instead
16735          * @static
16736          */
16737         getPosX: function(el) {
16738             return Roo.lib.Dom.getX(el);
16739         },
16740
16741         /**
16742          * Returns the Y position of an html element
16743          * @method getPosY
16744          * @param el the element for which to get the position
16745          * @return {int} the Y coordinate
16746          * @deprecated use Roo.lib.Dom.getY instead
16747          * @static
16748          */
16749         getPosY: function(el) {
16750             return Roo.lib.Dom.getY(el);
16751         },
16752
16753         /**
16754          * Swap two nodes.  In IE, we use the native method, for others we
16755          * emulate the IE behavior
16756          * @method swapNode
16757          * @param n1 the first node to swap
16758          * @param n2 the other node to swap
16759          * @static
16760          */
16761         swapNode: function(n1, n2) {
16762             if (n1.swapNode) {
16763                 n1.swapNode(n2);
16764             } else {
16765                 var p = n2.parentNode;
16766                 var s = n2.nextSibling;
16767
16768                 if (s == n1) {
16769                     p.insertBefore(n1, n2);
16770                 } else if (n2 == n1.nextSibling) {
16771                     p.insertBefore(n2, n1);
16772                 } else {
16773                     n1.parentNode.replaceChild(n2, n1);
16774                     p.insertBefore(n1, s);
16775                 }
16776             }
16777         },
16778
16779         /**
16780          * Returns the current scroll position
16781          * @method getScroll
16782          * @private
16783          * @static
16784          */
16785         getScroll: function () {
16786             var t, l, dde=document.documentElement, db=document.body;
16787             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16788                 t = dde.scrollTop;
16789                 l = dde.scrollLeft;
16790             } else if (db) {
16791                 t = db.scrollTop;
16792                 l = db.scrollLeft;
16793             } else {
16794
16795             }
16796             return { top: t, left: l };
16797         },
16798
16799         /**
16800          * Returns the specified element style property
16801          * @method getStyle
16802          * @param {HTMLElement} el          the element
16803          * @param {string}      styleProp   the style property
16804          * @return {string} The value of the style property
16805          * @deprecated use Roo.lib.Dom.getStyle
16806          * @static
16807          */
16808         getStyle: function(el, styleProp) {
16809             return Roo.fly(el).getStyle(styleProp);
16810         },
16811
16812         /**
16813          * Gets the scrollTop
16814          * @method getScrollTop
16815          * @return {int} the document's scrollTop
16816          * @static
16817          */
16818         getScrollTop: function () { return this.getScroll().top; },
16819
16820         /**
16821          * Gets the scrollLeft
16822          * @method getScrollLeft
16823          * @return {int} the document's scrollTop
16824          * @static
16825          */
16826         getScrollLeft: function () { return this.getScroll().left; },
16827
16828         /**
16829          * Sets the x/y position of an element to the location of the
16830          * target element.
16831          * @method moveToEl
16832          * @param {HTMLElement} moveEl      The element to move
16833          * @param {HTMLElement} targetEl    The position reference element
16834          * @static
16835          */
16836         moveToEl: function (moveEl, targetEl) {
16837             var aCoord = Roo.lib.Dom.getXY(targetEl);
16838             Roo.lib.Dom.setXY(moveEl, aCoord);
16839         },
16840
16841         /**
16842          * Numeric array sort function
16843          * @method numericSort
16844          * @static
16845          */
16846         numericSort: function(a, b) { return (a - b); },
16847
16848         /**
16849          * Internal counter
16850          * @property _timeoutCount
16851          * @private
16852          * @static
16853          */
16854         _timeoutCount: 0,
16855
16856         /**
16857          * Trying to make the load order less important.  Without this we get
16858          * an error if this file is loaded before the Event Utility.
16859          * @method _addListeners
16860          * @private
16861          * @static
16862          */
16863         _addListeners: function() {
16864             var DDM = Roo.dd.DDM;
16865             if ( Roo.lib.Event && document ) {
16866                 DDM._onLoad();
16867             } else {
16868                 if (DDM._timeoutCount > 2000) {
16869                 } else {
16870                     setTimeout(DDM._addListeners, 10);
16871                     if (document && document.body) {
16872                         DDM._timeoutCount += 1;
16873                     }
16874                 }
16875             }
16876         },
16877
16878         /**
16879          * Recursively searches the immediate parent and all child nodes for
16880          * the handle element in order to determine wheter or not it was
16881          * clicked.
16882          * @method handleWasClicked
16883          * @param node the html element to inspect
16884          * @static
16885          */
16886         handleWasClicked: function(node, id) {
16887             if (this.isHandle(id, node.id)) {
16888                 return true;
16889             } else {
16890                 // check to see if this is a text node child of the one we want
16891                 var p = node.parentNode;
16892
16893                 while (p) {
16894                     if (this.isHandle(id, p.id)) {
16895                         return true;
16896                     } else {
16897                         p = p.parentNode;
16898                     }
16899                 }
16900             }
16901
16902             return false;
16903         }
16904
16905     };
16906
16907 }();
16908
16909 // shorter alias, save a few bytes
16910 Roo.dd.DDM = Roo.dd.DragDropMgr;
16911 Roo.dd.DDM._addListeners();
16912
16913 }/*
16914  * Based on:
16915  * Ext JS Library 1.1.1
16916  * Copyright(c) 2006-2007, Ext JS, LLC.
16917  *
16918  * Originally Released Under LGPL - original licence link has changed is not relivant.
16919  *
16920  * Fork - LGPL
16921  * <script type="text/javascript">
16922  */
16923
16924 /**
16925  * @class Roo.dd.DD
16926  * A DragDrop implementation where the linked element follows the
16927  * mouse cursor during a drag.
16928  * @extends Roo.dd.DragDrop
16929  * @constructor
16930  * @param {String} id the id of the linked element
16931  * @param {String} sGroup the group of related DragDrop items
16932  * @param {object} config an object containing configurable attributes
16933  *                Valid properties for DD:
16934  *                    scroll
16935  */
16936 Roo.dd.DD = function(id, sGroup, config) {
16937     if (id) {
16938         this.init(id, sGroup, config);
16939     }
16940 };
16941
16942 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16943
16944     /**
16945      * When set to true, the utility automatically tries to scroll the browser
16946      * window wehn a drag and drop element is dragged near the viewport boundary.
16947      * Defaults to true.
16948      * @property scroll
16949      * @type boolean
16950      */
16951     scroll: true,
16952
16953     /**
16954      * Sets the pointer offset to the distance between the linked element's top
16955      * left corner and the location the element was clicked
16956      * @method autoOffset
16957      * @param {int} iPageX the X coordinate of the click
16958      * @param {int} iPageY the Y coordinate of the click
16959      */
16960     autoOffset: function(iPageX, iPageY) {
16961         var x = iPageX - this.startPageX;
16962         var y = iPageY - this.startPageY;
16963         this.setDelta(x, y);
16964     },
16965
16966     /**
16967      * Sets the pointer offset.  You can call this directly to force the
16968      * offset to be in a particular location (e.g., pass in 0,0 to set it
16969      * to the center of the object)
16970      * @method setDelta
16971      * @param {int} iDeltaX the distance from the left
16972      * @param {int} iDeltaY the distance from the top
16973      */
16974     setDelta: function(iDeltaX, iDeltaY) {
16975         this.deltaX = iDeltaX;
16976         this.deltaY = iDeltaY;
16977     },
16978
16979     /**
16980      * Sets the drag element to the location of the mousedown or click event,
16981      * maintaining the cursor location relative to the location on the element
16982      * that was clicked.  Override this if you want to place the element in a
16983      * location other than where the cursor is.
16984      * @method setDragElPos
16985      * @param {int} iPageX the X coordinate of the mousedown or drag event
16986      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16987      */
16988     setDragElPos: function(iPageX, iPageY) {
16989         // the first time we do this, we are going to check to make sure
16990         // the element has css positioning
16991
16992         var el = this.getDragEl();
16993         this.alignElWithMouse(el, iPageX, iPageY);
16994     },
16995
16996     /**
16997      * Sets the element to the location of the mousedown or click event,
16998      * maintaining the cursor location relative to the location on the element
16999      * that was clicked.  Override this if you want to place the element in a
17000      * location other than where the cursor is.
17001      * @method alignElWithMouse
17002      * @param {HTMLElement} el the element to move
17003      * @param {int} iPageX the X coordinate of the mousedown or drag event
17004      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17005      */
17006     alignElWithMouse: function(el, iPageX, iPageY) {
17007         var oCoord = this.getTargetCoord(iPageX, iPageY);
17008         var fly = el.dom ? el : Roo.fly(el);
17009         if (!this.deltaSetXY) {
17010             var aCoord = [oCoord.x, oCoord.y];
17011             fly.setXY(aCoord);
17012             var newLeft = fly.getLeft(true);
17013             var newTop  = fly.getTop(true);
17014             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17015         } else {
17016             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17017         }
17018
17019         this.cachePosition(oCoord.x, oCoord.y);
17020         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17021         return oCoord;
17022     },
17023
17024     /**
17025      * Saves the most recent position so that we can reset the constraints and
17026      * tick marks on-demand.  We need to know this so that we can calculate the
17027      * number of pixels the element is offset from its original position.
17028      * @method cachePosition
17029      * @param iPageX the current x position (optional, this just makes it so we
17030      * don't have to look it up again)
17031      * @param iPageY the current y position (optional, this just makes it so we
17032      * don't have to look it up again)
17033      */
17034     cachePosition: function(iPageX, iPageY) {
17035         if (iPageX) {
17036             this.lastPageX = iPageX;
17037             this.lastPageY = iPageY;
17038         } else {
17039             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17040             this.lastPageX = aCoord[0];
17041             this.lastPageY = aCoord[1];
17042         }
17043     },
17044
17045     /**
17046      * Auto-scroll the window if the dragged object has been moved beyond the
17047      * visible window boundary.
17048      * @method autoScroll
17049      * @param {int} x the drag element's x position
17050      * @param {int} y the drag element's y position
17051      * @param {int} h the height of the drag element
17052      * @param {int} w the width of the drag element
17053      * @private
17054      */
17055     autoScroll: function(x, y, h, w) {
17056
17057         if (this.scroll) {
17058             // The client height
17059             var clientH = Roo.lib.Dom.getViewWidth();
17060
17061             // The client width
17062             var clientW = Roo.lib.Dom.getViewHeight();
17063
17064             // The amt scrolled down
17065             var st = this.DDM.getScrollTop();
17066
17067             // The amt scrolled right
17068             var sl = this.DDM.getScrollLeft();
17069
17070             // Location of the bottom of the element
17071             var bot = h + y;
17072
17073             // Location of the right of the element
17074             var right = w + x;
17075
17076             // The distance from the cursor to the bottom of the visible area,
17077             // adjusted so that we don't scroll if the cursor is beyond the
17078             // element drag constraints
17079             var toBot = (clientH + st - y - this.deltaY);
17080
17081             // The distance from the cursor to the right of the visible area
17082             var toRight = (clientW + sl - x - this.deltaX);
17083
17084
17085             // How close to the edge the cursor must be before we scroll
17086             // var thresh = (document.all) ? 100 : 40;
17087             var thresh = 40;
17088
17089             // How many pixels to scroll per autoscroll op.  This helps to reduce
17090             // clunky scrolling. IE is more sensitive about this ... it needs this
17091             // value to be higher.
17092             var scrAmt = (document.all) ? 80 : 30;
17093
17094             // Scroll down if we are near the bottom of the visible page and the
17095             // obj extends below the crease
17096             if ( bot > clientH && toBot < thresh ) {
17097                 window.scrollTo(sl, st + scrAmt);
17098             }
17099
17100             // Scroll up if the window is scrolled down and the top of the object
17101             // goes above the top border
17102             if ( y < st && st > 0 && y - st < thresh ) {
17103                 window.scrollTo(sl, st - scrAmt);
17104             }
17105
17106             // Scroll right if the obj is beyond the right border and the cursor is
17107             // near the border.
17108             if ( right > clientW && toRight < thresh ) {
17109                 window.scrollTo(sl + scrAmt, st);
17110             }
17111
17112             // Scroll left if the window has been scrolled to the right and the obj
17113             // extends past the left border
17114             if ( x < sl && sl > 0 && x - sl < thresh ) {
17115                 window.scrollTo(sl - scrAmt, st);
17116             }
17117         }
17118     },
17119
17120     /**
17121      * Finds the location the element should be placed if we want to move
17122      * it to where the mouse location less the click offset would place us.
17123      * @method getTargetCoord
17124      * @param {int} iPageX the X coordinate of the click
17125      * @param {int} iPageY the Y coordinate of the click
17126      * @return an object that contains the coordinates (Object.x and Object.y)
17127      * @private
17128      */
17129     getTargetCoord: function(iPageX, iPageY) {
17130
17131
17132         var x = iPageX - this.deltaX;
17133         var y = iPageY - this.deltaY;
17134
17135         if (this.constrainX) {
17136             if (x < this.minX) { x = this.minX; }
17137             if (x > this.maxX) { x = this.maxX; }
17138         }
17139
17140         if (this.constrainY) {
17141             if (y < this.minY) { y = this.minY; }
17142             if (y > this.maxY) { y = this.maxY; }
17143         }
17144
17145         x = this.getTick(x, this.xTicks);
17146         y = this.getTick(y, this.yTicks);
17147
17148
17149         return {x:x, y:y};
17150     },
17151
17152     /*
17153      * Sets up config options specific to this class. Overrides
17154      * Roo.dd.DragDrop, but all versions of this method through the
17155      * inheritance chain are called
17156      */
17157     applyConfig: function() {
17158         Roo.dd.DD.superclass.applyConfig.call(this);
17159         this.scroll = (this.config.scroll !== false);
17160     },
17161
17162     /*
17163      * Event that fires prior to the onMouseDown event.  Overrides
17164      * Roo.dd.DragDrop.
17165      */
17166     b4MouseDown: function(e) {
17167         // this.resetConstraints();
17168         this.autoOffset(e.getPageX(),
17169                             e.getPageY());
17170     },
17171
17172     /*
17173      * Event that fires prior to the onDrag event.  Overrides
17174      * Roo.dd.DragDrop.
17175      */
17176     b4Drag: function(e) {
17177         this.setDragElPos(e.getPageX(),
17178                             e.getPageY());
17179     },
17180
17181     toString: function() {
17182         return ("DD " + this.id);
17183     }
17184
17185     //////////////////////////////////////////////////////////////////////////
17186     // Debugging ygDragDrop events that can be overridden
17187     //////////////////////////////////////////////////////////////////////////
17188     /*
17189     startDrag: function(x, y) {
17190     },
17191
17192     onDrag: function(e) {
17193     },
17194
17195     onDragEnter: function(e, id) {
17196     },
17197
17198     onDragOver: function(e, id) {
17199     },
17200
17201     onDragOut: function(e, id) {
17202     },
17203
17204     onDragDrop: function(e, id) {
17205     },
17206
17207     endDrag: function(e) {
17208     }
17209
17210     */
17211
17212 });/*
17213  * Based on:
17214  * Ext JS Library 1.1.1
17215  * Copyright(c) 2006-2007, Ext JS, LLC.
17216  *
17217  * Originally Released Under LGPL - original licence link has changed is not relivant.
17218  *
17219  * Fork - LGPL
17220  * <script type="text/javascript">
17221  */
17222
17223 /**
17224  * @class Roo.dd.DDProxy
17225  * A DragDrop implementation that inserts an empty, bordered div into
17226  * the document that follows the cursor during drag operations.  At the time of
17227  * the click, the frame div is resized to the dimensions of the linked html
17228  * element, and moved to the exact location of the linked element.
17229  *
17230  * References to the "frame" element refer to the single proxy element that
17231  * was created to be dragged in place of all DDProxy elements on the
17232  * page.
17233  *
17234  * @extends Roo.dd.DD
17235  * @constructor
17236  * @param {String} id the id of the linked html element
17237  * @param {String} sGroup the group of related DragDrop objects
17238  * @param {object} config an object containing configurable attributes
17239  *                Valid properties for DDProxy in addition to those in DragDrop:
17240  *                   resizeFrame, centerFrame, dragElId
17241  */
17242 Roo.dd.DDProxy = function(id, sGroup, config) {
17243     if (id) {
17244         this.init(id, sGroup, config);
17245         this.initFrame();
17246     }
17247 };
17248
17249 /**
17250  * The default drag frame div id
17251  * @property Roo.dd.DDProxy.dragElId
17252  * @type String
17253  * @static
17254  */
17255 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17256
17257 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17258
17259     /**
17260      * By default we resize the drag frame to be the same size as the element
17261      * we want to drag (this is to get the frame effect).  We can turn it off
17262      * if we want a different behavior.
17263      * @property resizeFrame
17264      * @type boolean
17265      */
17266     resizeFrame: true,
17267
17268     /**
17269      * By default the frame is positioned exactly where the drag element is, so
17270      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17271      * you do not have constraints on the obj is to have the drag frame centered
17272      * around the cursor.  Set centerFrame to true for this effect.
17273      * @property centerFrame
17274      * @type boolean
17275      */
17276     centerFrame: false,
17277
17278     /**
17279      * Creates the proxy element if it does not yet exist
17280      * @method createFrame
17281      */
17282     createFrame: function() {
17283         var self = this;
17284         var body = document.body;
17285
17286         if (!body || !body.firstChild) {
17287             setTimeout( function() { self.createFrame(); }, 50 );
17288             return;
17289         }
17290
17291         var div = this.getDragEl();
17292
17293         if (!div) {
17294             div    = document.createElement("div");
17295             div.id = this.dragElId;
17296             var s  = div.style;
17297
17298             s.position   = "absolute";
17299             s.visibility = "hidden";
17300             s.cursor     = "move";
17301             s.border     = "2px solid #aaa";
17302             s.zIndex     = 999;
17303
17304             // appendChild can blow up IE if invoked prior to the window load event
17305             // while rendering a table.  It is possible there are other scenarios
17306             // that would cause this to happen as well.
17307             body.insertBefore(div, body.firstChild);
17308         }
17309     },
17310
17311     /**
17312      * Initialization for the drag frame element.  Must be called in the
17313      * constructor of all subclasses
17314      * @method initFrame
17315      */
17316     initFrame: function() {
17317         this.createFrame();
17318     },
17319
17320     applyConfig: function() {
17321         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17322
17323         this.resizeFrame = (this.config.resizeFrame !== false);
17324         this.centerFrame = (this.config.centerFrame);
17325         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17326     },
17327
17328     /**
17329      * Resizes the drag frame to the dimensions of the clicked object, positions
17330      * it over the object, and finally displays it
17331      * @method showFrame
17332      * @param {int} iPageX X click position
17333      * @param {int} iPageY Y click position
17334      * @private
17335      */
17336     showFrame: function(iPageX, iPageY) {
17337         var el = this.getEl();
17338         var dragEl = this.getDragEl();
17339         var s = dragEl.style;
17340
17341         this._resizeProxy();
17342
17343         if (this.centerFrame) {
17344             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17345                            Math.round(parseInt(s.height, 10)/2) );
17346         }
17347
17348         this.setDragElPos(iPageX, iPageY);
17349
17350         Roo.fly(dragEl).show();
17351     },
17352
17353     /**
17354      * The proxy is automatically resized to the dimensions of the linked
17355      * element when a drag is initiated, unless resizeFrame is set to false
17356      * @method _resizeProxy
17357      * @private
17358      */
17359     _resizeProxy: function() {
17360         if (this.resizeFrame) {
17361             var el = this.getEl();
17362             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17363         }
17364     },
17365
17366     // overrides Roo.dd.DragDrop
17367     b4MouseDown: function(e) {
17368         var x = e.getPageX();
17369         var y = e.getPageY();
17370         this.autoOffset(x, y);
17371         this.setDragElPos(x, y);
17372     },
17373
17374     // overrides Roo.dd.DragDrop
17375     b4StartDrag: function(x, y) {
17376         // show the drag frame
17377         this.showFrame(x, y);
17378     },
17379
17380     // overrides Roo.dd.DragDrop
17381     b4EndDrag: function(e) {
17382         Roo.fly(this.getDragEl()).hide();
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     // By default we try to move the element to the last location of the frame.
17387     // This is so that the default behavior mirrors that of Roo.dd.DD.
17388     endDrag: function(e) {
17389
17390         var lel = this.getEl();
17391         var del = this.getDragEl();
17392
17393         // Show the drag frame briefly so we can get its position
17394         del.style.visibility = "";
17395
17396         this.beforeMove();
17397         // Hide the linked element before the move to get around a Safari
17398         // rendering bug.
17399         lel.style.visibility = "hidden";
17400         Roo.dd.DDM.moveToEl(lel, del);
17401         del.style.visibility = "hidden";
17402         lel.style.visibility = "";
17403
17404         this.afterDrag();
17405     },
17406
17407     beforeMove : function(){
17408
17409     },
17410
17411     afterDrag : function(){
17412
17413     },
17414
17415     toString: function() {
17416         return ("DDProxy " + this.id);
17417     }
17418
17419 });
17420 /*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430
17431  /**
17432  * @class Roo.dd.DDTarget
17433  * A DragDrop implementation that does not move, but can be a drop
17434  * target.  You would get the same result by simply omitting implementation
17435  * for the event callbacks, but this way we reduce the processing cost of the
17436  * event listener and the callbacks.
17437  * @extends Roo.dd.DragDrop
17438  * @constructor
17439  * @param {String} id the id of the element that is a drop target
17440  * @param {String} sGroup the group of related DragDrop objects
17441  * @param {object} config an object containing configurable attributes
17442  *                 Valid properties for DDTarget in addition to those in
17443  *                 DragDrop:
17444  *                    none
17445  */
17446 Roo.dd.DDTarget = function(id, sGroup, config) {
17447     if (id) {
17448         this.initTarget(id, sGroup, config);
17449     }
17450     if (config.listeners || config.events) { 
17451        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17452             listeners : config.listeners || {}, 
17453             events : config.events || {} 
17454         });    
17455     }
17456 };
17457
17458 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17459 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17460     toString: function() {
17461         return ("DDTarget " + this.id);
17462     }
17463 });
17464 /*
17465  * Based on:
17466  * Ext JS Library 1.1.1
17467  * Copyright(c) 2006-2007, Ext JS, LLC.
17468  *
17469  * Originally Released Under LGPL - original licence link has changed is not relivant.
17470  *
17471  * Fork - LGPL
17472  * <script type="text/javascript">
17473  */
17474  
17475
17476 /**
17477  * @class Roo.dd.ScrollManager
17478  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17479  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17480  * @singleton
17481  */
17482 Roo.dd.ScrollManager = function(){
17483     var ddm = Roo.dd.DragDropMgr;
17484     var els = {};
17485     var dragEl = null;
17486     var proc = {};
17487     
17488     var onStop = function(e){
17489         dragEl = null;
17490         clearProc();
17491     };
17492     
17493     var triggerRefresh = function(){
17494         if(ddm.dragCurrent){
17495              ddm.refreshCache(ddm.dragCurrent.groups);
17496         }
17497     };
17498     
17499     var doScroll = function(){
17500         if(ddm.dragCurrent){
17501             var dds = Roo.dd.ScrollManager;
17502             if(!dds.animate){
17503                 if(proc.el.scroll(proc.dir, dds.increment)){
17504                     triggerRefresh();
17505                 }
17506             }else{
17507                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17508             }
17509         }
17510     };
17511     
17512     var clearProc = function(){
17513         if(proc.id){
17514             clearInterval(proc.id);
17515         }
17516         proc.id = 0;
17517         proc.el = null;
17518         proc.dir = "";
17519     };
17520     
17521     var startProc = function(el, dir){
17522         clearProc();
17523         proc.el = el;
17524         proc.dir = dir;
17525         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17526     };
17527     
17528     var onFire = function(e, isDrop){
17529         if(isDrop || !ddm.dragCurrent){ return; }
17530         var dds = Roo.dd.ScrollManager;
17531         if(!dragEl || dragEl != ddm.dragCurrent){
17532             dragEl = ddm.dragCurrent;
17533             // refresh regions on drag start
17534             dds.refreshCache();
17535         }
17536         
17537         var xy = Roo.lib.Event.getXY(e);
17538         var pt = new Roo.lib.Point(xy[0], xy[1]);
17539         for(var id in els){
17540             var el = els[id], r = el._region;
17541             if(r && r.contains(pt) && el.isScrollable()){
17542                 if(r.bottom - pt.y <= dds.thresh){
17543                     if(proc.el != el){
17544                         startProc(el, "down");
17545                     }
17546                     return;
17547                 }else if(r.right - pt.x <= dds.thresh){
17548                     if(proc.el != el){
17549                         startProc(el, "left");
17550                     }
17551                     return;
17552                 }else if(pt.y - r.top <= dds.thresh){
17553                     if(proc.el != el){
17554                         startProc(el, "up");
17555                     }
17556                     return;
17557                 }else if(pt.x - r.left <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "right");
17560                     }
17561                     return;
17562                 }
17563             }
17564         }
17565         clearProc();
17566     };
17567     
17568     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17569     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17570     
17571     return {
17572         /**
17573          * Registers new overflow element(s) to auto scroll
17574          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17575          */
17576         register : function(el){
17577             if(el instanceof Array){
17578                 for(var i = 0, len = el.length; i < len; i++) {
17579                         this.register(el[i]);
17580                 }
17581             }else{
17582                 el = Roo.get(el);
17583                 els[el.id] = el;
17584             }
17585         },
17586         
17587         /**
17588          * Unregisters overflow element(s) so they are no longer scrolled
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17590          */
17591         unregister : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.unregister(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 delete els[el.id];
17599             }
17600         },
17601         
17602         /**
17603          * The number of pixels from the edge of a container the pointer needs to be to 
17604          * trigger scrolling (defaults to 25)
17605          * @type Number
17606          */
17607         thresh : 25,
17608         
17609         /**
17610          * The number of pixels to scroll in each scroll increment (defaults to 50)
17611          * @type Number
17612          */
17613         increment : 100,
17614         
17615         /**
17616          * The frequency of scrolls in milliseconds (defaults to 500)
17617          * @type Number
17618          */
17619         frequency : 500,
17620         
17621         /**
17622          * True to animate the scroll (defaults to true)
17623          * @type Boolean
17624          */
17625         animate: true,
17626         
17627         /**
17628          * The animation duration in seconds - 
17629          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17630          * @type Number
17631          */
17632         animDuration: .4,
17633         
17634         /**
17635          * Manually trigger a cache refresh.
17636          */
17637         refreshCache : function(){
17638             for(var id in els){
17639                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17640                     els[id]._region = els[id].getRegion();
17641                 }
17642             }
17643         }
17644     };
17645 }();/*
17646  * Based on:
17647  * Ext JS Library 1.1.1
17648  * Copyright(c) 2006-2007, Ext JS, LLC.
17649  *
17650  * Originally Released Under LGPL - original licence link has changed is not relivant.
17651  *
17652  * Fork - LGPL
17653  * <script type="text/javascript">
17654  */
17655  
17656
17657 /**
17658  * @class Roo.dd.Registry
17659  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17660  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17661  * @singleton
17662  */
17663 Roo.dd.Registry = function(){
17664     var elements = {}; 
17665     var handles = {}; 
17666     var autoIdSeed = 0;
17667
17668     var getId = function(el, autogen){
17669         if(typeof el == "string"){
17670             return el;
17671         }
17672         var id = el.id;
17673         if(!id && autogen !== false){
17674             id = "roodd-" + (++autoIdSeed);
17675             el.id = id;
17676         }
17677         return id;
17678     };
17679     
17680     return {
17681     /**
17682      * Register a drag drop element
17683      * @param {String|HTMLElement} element The id or DOM node to register
17684      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17685      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17686      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17687      * populated in the data object (if applicable):
17688      * <pre>
17689 Value      Description<br />
17690 ---------  ------------------------------------------<br />
17691 handles    Array of DOM nodes that trigger dragging<br />
17692            for the element being registered<br />
17693 isHandle   True if the element passed in triggers<br />
17694            dragging itself, else false
17695 </pre>
17696      */
17697         register : function(el, data){
17698             data = data || {};
17699             if(typeof el == "string"){
17700                 el = document.getElementById(el);
17701             }
17702             data.ddel = el;
17703             elements[getId(el)] = data;
17704             if(data.isHandle !== false){
17705                 handles[data.ddel.id] = data;
17706             }
17707             if(data.handles){
17708                 var hs = data.handles;
17709                 for(var i = 0, len = hs.length; i < len; i++){
17710                         handles[getId(hs[i])] = data;
17711                 }
17712             }
17713         },
17714
17715     /**
17716      * Unregister a drag drop element
17717      * @param {String|HTMLElement}  element The id or DOM node to unregister
17718      */
17719         unregister : function(el){
17720             var id = getId(el, false);
17721             var data = elements[id];
17722             if(data){
17723                 delete elements[id];
17724                 if(data.handles){
17725                     var hs = data.handles;
17726                     for(var i = 0, len = hs.length; i < len; i++){
17727                         delete handles[getId(hs[i], false)];
17728                     }
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Returns the handle registered for a DOM Node by id
17735      * @param {String|HTMLElement} id The DOM node or id to look up
17736      * @return {Object} handle The custom handle data
17737      */
17738         getHandle : function(id){
17739             if(typeof id != "string"){ // must be element?
17740                 id = id.id;
17741             }
17742             return handles[id];
17743         },
17744
17745     /**
17746      * Returns the handle that is registered for the DOM node that is the target of the event
17747      * @param {Event} e The event
17748      * @return {Object} handle The custom handle data
17749      */
17750         getHandleFromEvent : function(e){
17751             var t = Roo.lib.Event.getTarget(e);
17752             return t ? handles[t.id] : null;
17753         },
17754
17755     /**
17756      * Returns a custom data object that is registered for a DOM node by id
17757      * @param {String|HTMLElement} id The DOM node or id to look up
17758      * @return {Object} data The custom data
17759      */
17760         getTarget : function(id){
17761             if(typeof id != "string"){ // must be element?
17762                 id = id.id;
17763             }
17764             return elements[id];
17765         },
17766
17767     /**
17768      * Returns a custom data object that is registered for the DOM node that is the target of the event
17769      * @param {Event} e The event
17770      * @return {Object} data The custom data
17771      */
17772         getTargetFromEvent : function(e){
17773             var t = Roo.lib.Event.getTarget(e);
17774             return t ? elements[t.id] || handles[t.id] : null;
17775         }
17776     };
17777 }();/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787  
17788
17789 /**
17790  * @class Roo.dd.StatusProxy
17791  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17792  * default drag proxy used by all Roo.dd components.
17793  * @constructor
17794  * @param {Object} config
17795  */
17796 Roo.dd.StatusProxy = function(config){
17797     Roo.apply(this, config);
17798     this.id = this.id || Roo.id();
17799     this.el = new Roo.Layer({
17800         dh: {
17801             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17802                 {tag: "div", cls: "x-dd-drop-icon"},
17803                 {tag: "div", cls: "x-dd-drag-ghost"}
17804             ]
17805         }, 
17806         shadow: !config || config.shadow !== false
17807     });
17808     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17809     this.dropStatus = this.dropNotAllowed;
17810 };
17811
17812 Roo.dd.StatusProxy.prototype = {
17813     /**
17814      * @cfg {String} dropAllowed
17815      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17816      */
17817     dropAllowed : "x-dd-drop-ok",
17818     /**
17819      * @cfg {String} dropNotAllowed
17820      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17821      */
17822     dropNotAllowed : "x-dd-drop-nodrop",
17823
17824     /**
17825      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17826      * over the current target element.
17827      * @param {String} cssClass The css class for the new drop status indicator image
17828      */
17829     setStatus : function(cssClass){
17830         cssClass = cssClass || this.dropNotAllowed;
17831         if(this.dropStatus != cssClass){
17832             this.el.replaceClass(this.dropStatus, cssClass);
17833             this.dropStatus = cssClass;
17834         }
17835     },
17836
17837     /**
17838      * Resets the status indicator to the default dropNotAllowed value
17839      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17840      */
17841     reset : function(clearGhost){
17842         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17843         this.dropStatus = this.dropNotAllowed;
17844         if(clearGhost){
17845             this.ghost.update("");
17846         }
17847     },
17848
17849     /**
17850      * Updates the contents of the ghost element
17851      * @param {String} html The html that will replace the current innerHTML of the ghost element
17852      */
17853     update : function(html){
17854         if(typeof html == "string"){
17855             this.ghost.update(html);
17856         }else{
17857             this.ghost.update("");
17858             html.style.margin = "0";
17859             this.ghost.dom.appendChild(html);
17860         }
17861         // ensure float = none set?? cant remember why though.
17862         var el = this.ghost.dom.firstChild;
17863                 if(el){
17864                         Roo.fly(el).setStyle('float', 'none');
17865                 }
17866     },
17867     
17868     /**
17869      * Returns the underlying proxy {@link Roo.Layer}
17870      * @return {Roo.Layer} el
17871     */
17872     getEl : function(){
17873         return this.el;
17874     },
17875
17876     /**
17877      * Returns the ghost element
17878      * @return {Roo.Element} el
17879      */
17880     getGhost : function(){
17881         return this.ghost;
17882     },
17883
17884     /**
17885      * Hides the proxy
17886      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17887      */
17888     hide : function(clear){
17889         this.el.hide();
17890         if(clear){
17891             this.reset(true);
17892         }
17893     },
17894
17895     /**
17896      * Stops the repair animation if it's currently running
17897      */
17898     stop : function(){
17899         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17900             this.anim.stop();
17901         }
17902     },
17903
17904     /**
17905      * Displays this proxy
17906      */
17907     show : function(){
17908         this.el.show();
17909     },
17910
17911     /**
17912      * Force the Layer to sync its shadow and shim positions to the element
17913      */
17914     sync : function(){
17915         this.el.sync();
17916     },
17917
17918     /**
17919      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17920      * invalid drop operation by the item being dragged.
17921      * @param {Array} xy The XY position of the element ([x, y])
17922      * @param {Function} callback The function to call after the repair is complete
17923      * @param {Object} scope The scope in which to execute the callback
17924      */
17925     repair : function(xy, callback, scope){
17926         this.callback = callback;
17927         this.scope = scope;
17928         if(xy && this.animRepair !== false){
17929             this.el.addClass("x-dd-drag-repair");
17930             this.el.hideUnders(true);
17931             this.anim = this.el.shift({
17932                 duration: this.repairDuration || .5,
17933                 easing: 'easeOut',
17934                 xy: xy,
17935                 stopFx: true,
17936                 callback: this.afterRepair,
17937                 scope: this
17938             });
17939         }else{
17940             this.afterRepair();
17941         }
17942     },
17943
17944     // private
17945     afterRepair : function(){
17946         this.hide(true);
17947         if(typeof this.callback == "function"){
17948             this.callback.call(this.scope || this);
17949         }
17950         this.callback = null;
17951         this.scope = null;
17952     }
17953 };/*
17954  * Based on:
17955  * Ext JS Library 1.1.1
17956  * Copyright(c) 2006-2007, Ext JS, LLC.
17957  *
17958  * Originally Released Under LGPL - original licence link has changed is not relivant.
17959  *
17960  * Fork - LGPL
17961  * <script type="text/javascript">
17962  */
17963
17964 /**
17965  * @class Roo.dd.DragSource
17966  * @extends Roo.dd.DDProxy
17967  * A simple class that provides the basic implementation needed to make any element draggable.
17968  * @constructor
17969  * @param {String/HTMLElement/Element} el The container element
17970  * @param {Object} config
17971  */
17972 Roo.dd.DragSource = function(el, config){
17973     this.el = Roo.get(el);
17974     this.dragData = {};
17975     
17976     Roo.apply(this, config);
17977     
17978     if(!this.proxy){
17979         this.proxy = new Roo.dd.StatusProxy();
17980     }
17981
17982     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17983           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17984     
17985     this.dragging = false;
17986 };
17987
17988 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17989     /**
17990      * @cfg {String} dropAllowed
17991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17992      */
17993     dropAllowed : "x-dd-drop-ok",
17994     /**
17995      * @cfg {String} dropNotAllowed
17996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17997      */
17998     dropNotAllowed : "x-dd-drop-nodrop",
17999
18000     /**
18001      * Returns the data object associated with this drag source
18002      * @return {Object} data An object containing arbitrary data
18003      */
18004     getDragData : function(e){
18005         return this.dragData;
18006     },
18007
18008     // private
18009     onDragEnter : function(e, id){
18010         var target = Roo.dd.DragDropMgr.getDDById(id);
18011         this.cachedTarget = target;
18012         if(this.beforeDragEnter(target, e, id) !== false){
18013             if(target.isNotifyTarget){
18014                 var status = target.notifyEnter(this, e, this.dragData);
18015                 this.proxy.setStatus(status);
18016             }else{
18017                 this.proxy.setStatus(this.dropAllowed);
18018             }
18019             
18020             if(this.afterDragEnter){
18021                 /**
18022                  * An empty function by default, but provided so that you can perform a custom action
18023                  * when the dragged item enters the drop target by providing an implementation.
18024                  * @param {Roo.dd.DragDrop} target The drop target
18025                  * @param {Event} e The event object
18026                  * @param {String} id The id of the dragged element
18027                  * @method afterDragEnter
18028                  */
18029                 this.afterDragEnter(target, e, id);
18030             }
18031         }
18032     },
18033
18034     /**
18035      * An empty function by default, but provided so that you can perform a custom action
18036      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18037      * @param {Roo.dd.DragDrop} target The drop target
18038      * @param {Event} e The event object
18039      * @param {String} id The id of the dragged element
18040      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18041      */
18042     beforeDragEnter : function(target, e, id){
18043         return true;
18044     },
18045
18046     // private
18047     alignElWithMouse: function() {
18048         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18049         this.proxy.sync();
18050     },
18051
18052     // private
18053     onDragOver : function(e, id){
18054         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18055         if(this.beforeDragOver(target, e, id) !== false){
18056             if(target.isNotifyTarget){
18057                 var status = target.notifyOver(this, e, this.dragData);
18058                 this.proxy.setStatus(status);
18059             }
18060
18061             if(this.afterDragOver){
18062                 /**
18063                  * An empty function by default, but provided so that you can perform a custom action
18064                  * while the dragged item is over the drop target by providing an implementation.
18065                  * @param {Roo.dd.DragDrop} target The drop target
18066                  * @param {Event} e The event object
18067                  * @param {String} id The id of the dragged element
18068                  * @method afterDragOver
18069                  */
18070                 this.afterDragOver(target, e, id);
18071             }
18072         }
18073     },
18074
18075     /**
18076      * An empty function by default, but provided so that you can perform a custom action
18077      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18078      * @param {Roo.dd.DragDrop} target The drop target
18079      * @param {Event} e The event object
18080      * @param {String} id The id of the dragged element
18081      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18082      */
18083     beforeDragOver : function(target, e, id){
18084         return true;
18085     },
18086
18087     // private
18088     onDragOut : function(e, id){
18089         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18090         if(this.beforeDragOut(target, e, id) !== false){
18091             if(target.isNotifyTarget){
18092                 target.notifyOut(this, e, this.dragData);
18093             }
18094             this.proxy.reset();
18095             if(this.afterDragOut){
18096                 /**
18097                  * An empty function by default, but provided so that you can perform a custom action
18098                  * after the dragged item is dragged out of the target without dropping.
18099                  * @param {Roo.dd.DragDrop} target The drop target
18100                  * @param {Event} e The event object
18101                  * @param {String} id The id of the dragged element
18102                  * @method afterDragOut
18103                  */
18104                 this.afterDragOut(target, e, id);
18105             }
18106         }
18107         this.cachedTarget = null;
18108     },
18109
18110     /**
18111      * An empty function by default, but provided so that you can perform a custom action before the dragged
18112      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18113      * @param {Roo.dd.DragDrop} target The drop target
18114      * @param {Event} e The event object
18115      * @param {String} id The id of the dragged element
18116      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18117      */
18118     beforeDragOut : function(target, e, id){
18119         return true;
18120     },
18121     
18122     // private
18123     onDragDrop : function(e, id){
18124         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18125         if(this.beforeDragDrop(target, e, id) !== false){
18126             if(target.isNotifyTarget){
18127                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18128                     this.onValidDrop(target, e, id);
18129                 }else{
18130                     this.onInvalidDrop(target, e, id);
18131                 }
18132             }else{
18133                 this.onValidDrop(target, e, id);
18134             }
18135             
18136             if(this.afterDragDrop){
18137                 /**
18138                  * An empty function by default, but provided so that you can perform a custom action
18139                  * after a valid drag drop has occurred by providing an implementation.
18140                  * @param {Roo.dd.DragDrop} target The drop target
18141                  * @param {Event} e The event object
18142                  * @param {String} id The id of the dropped element
18143                  * @method afterDragDrop
18144                  */
18145                 this.afterDragDrop(target, e, id);
18146             }
18147         }
18148         delete this.cachedTarget;
18149     },
18150
18151     /**
18152      * An empty function by default, but provided so that you can perform a custom action before the dragged
18153      * item is dropped onto the target and optionally cancel the onDragDrop.
18154      * @param {Roo.dd.DragDrop} target The drop target
18155      * @param {Event} e The event object
18156      * @param {String} id The id of the dragged element
18157      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18158      */
18159     beforeDragDrop : function(target, e, id){
18160         return true;
18161     },
18162
18163     // private
18164     onValidDrop : function(target, e, id){
18165         this.hideProxy();
18166         if(this.afterValidDrop){
18167             /**
18168              * An empty function by default, but provided so that you can perform a custom action
18169              * after a valid drop has occurred by providing an implementation.
18170              * @param {Object} target The target DD 
18171              * @param {Event} e The event object
18172              * @param {String} id The id of the dropped element
18173              * @method afterInvalidDrop
18174              */
18175             this.afterValidDrop(target, e, id);
18176         }
18177     },
18178
18179     // private
18180     getRepairXY : function(e, data){
18181         return this.el.getXY();  
18182     },
18183
18184     // private
18185     onInvalidDrop : function(target, e, id){
18186         this.beforeInvalidDrop(target, e, id);
18187         if(this.cachedTarget){
18188             if(this.cachedTarget.isNotifyTarget){
18189                 this.cachedTarget.notifyOut(this, e, this.dragData);
18190             }
18191             this.cacheTarget = null;
18192         }
18193         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18194
18195         if(this.afterInvalidDrop){
18196             /**
18197              * An empty function by default, but provided so that you can perform a custom action
18198              * after an invalid drop has occurred by providing an implementation.
18199              * @param {Event} e The event object
18200              * @param {String} id The id of the dropped element
18201              * @method afterInvalidDrop
18202              */
18203             this.afterInvalidDrop(e, id);
18204         }
18205     },
18206
18207     // private
18208     afterRepair : function(){
18209         if(Roo.enableFx){
18210             this.el.highlight(this.hlColor || "c3daf9");
18211         }
18212         this.dragging = false;
18213     },
18214
18215     /**
18216      * An empty function by default, but provided so that you can perform a custom action after an invalid
18217      * drop has occurred.
18218      * @param {Roo.dd.DragDrop} target The drop target
18219      * @param {Event} e The event object
18220      * @param {String} id The id of the dragged element
18221      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18222      */
18223     beforeInvalidDrop : function(target, e, id){
18224         return true;
18225     },
18226
18227     // private
18228     handleMouseDown : function(e){
18229         if(this.dragging) {
18230             return;
18231         }
18232         var data = this.getDragData(e);
18233         if(data && this.onBeforeDrag(data, e) !== false){
18234             this.dragData = data;
18235             this.proxy.stop();
18236             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18237         } 
18238     },
18239
18240     /**
18241      * An empty function by default, but provided so that you can perform a custom action before the initial
18242      * drag event begins and optionally cancel it.
18243      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18244      * @param {Event} e The event object
18245      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18246      */
18247     onBeforeDrag : function(data, e){
18248         return true;
18249     },
18250
18251     /**
18252      * An empty function by default, but provided so that you can perform a custom action once the initial
18253      * drag event has begun.  The drag cannot be canceled from this function.
18254      * @param {Number} x The x position of the click on the dragged object
18255      * @param {Number} y The y position of the click on the dragged object
18256      */
18257     onStartDrag : Roo.emptyFn,
18258
18259     // private - YUI override
18260     startDrag : function(x, y){
18261         this.proxy.reset();
18262         this.dragging = true;
18263         this.proxy.update("");
18264         this.onInitDrag(x, y);
18265         this.proxy.show();
18266     },
18267
18268     // private
18269     onInitDrag : function(x, y){
18270         var clone = this.el.dom.cloneNode(true);
18271         clone.id = Roo.id(); // prevent duplicate ids
18272         this.proxy.update(clone);
18273         this.onStartDrag(x, y);
18274         return true;
18275     },
18276
18277     /**
18278      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18279      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18280      */
18281     getProxy : function(){
18282         return this.proxy;  
18283     },
18284
18285     /**
18286      * Hides the drag source's {@link Roo.dd.StatusProxy}
18287      */
18288     hideProxy : function(){
18289         this.proxy.hide();  
18290         this.proxy.reset(true);
18291         this.dragging = false;
18292     },
18293
18294     // private
18295     triggerCacheRefresh : function(){
18296         Roo.dd.DDM.refreshCache(this.groups);
18297     },
18298
18299     // private - override to prevent hiding
18300     b4EndDrag: function(e) {
18301     },
18302
18303     // private - override to prevent moving
18304     endDrag : function(e){
18305         this.onEndDrag(this.dragData, e);
18306     },
18307
18308     // private
18309     onEndDrag : function(data, e){
18310     },
18311     
18312     // private - pin to cursor
18313     autoOffset : function(x, y) {
18314         this.setDelta(-12, -20);
18315     }    
18316 });/*
18317  * Based on:
18318  * Ext JS Library 1.1.1
18319  * Copyright(c) 2006-2007, Ext JS, LLC.
18320  *
18321  * Originally Released Under LGPL - original licence link has changed is not relivant.
18322  *
18323  * Fork - LGPL
18324  * <script type="text/javascript">
18325  */
18326
18327
18328 /**
18329  * @class Roo.dd.DropTarget
18330  * @extends Roo.dd.DDTarget
18331  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18332  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18333  * @constructor
18334  * @param {String/HTMLElement/Element} el The container element
18335  * @param {Object} config
18336  */
18337 Roo.dd.DropTarget = function(el, config){
18338     this.el = Roo.get(el);
18339     
18340     var listeners = false; ;
18341     if (config && config.listeners) {
18342         listeners= config.listeners;
18343         delete config.listeners;
18344     }
18345     Roo.apply(this, config);
18346     
18347     if(this.containerScroll){
18348         Roo.dd.ScrollManager.register(this.el);
18349     }
18350     this.addEvents( {
18351          /**
18352          * @scope Roo.dd.DropTarget
18353          */
18354          
18355          /**
18356          * @event enter
18357          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18358          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18359          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18360          * 
18361          * IMPORTANT : it should set this.overClass and this.dropAllowed
18362          * 
18363          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18364          * @param {Event} e The event
18365          * @param {Object} data An object containing arbitrary data supplied by the drag source
18366          */
18367         "enter" : true,
18368         
18369          /**
18370          * @event over
18371          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18372          * This method will be called on every mouse movement while the drag source is over the drop target.
18373          * This default implementation simply returns the dropAllowed config value.
18374          * 
18375          * IMPORTANT : it should set this.dropAllowed
18376          * 
18377          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18378          * @param {Event} e The event
18379          * @param {Object} data An object containing arbitrary data supplied by the drag source
18380          
18381          */
18382         "over" : true,
18383         /**
18384          * @event out
18385          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18386          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18387          * overClass (if any) from the drop element.
18388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18389          * @param {Event} e The event
18390          * @param {Object} data An object containing arbitrary data supplied by the drag source
18391          */
18392          "out" : true,
18393          
18394         /**
18395          * @event drop
18396          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18397          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18398          * implementation that does something to process the drop event and returns true so that the drag source's
18399          * repair action does not run.
18400          * 
18401          * IMPORTANT : it should set this.success
18402          * 
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          "drop" : true
18408     });
18409             
18410      
18411     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18412         this.el.dom, 
18413         this.ddGroup || this.group,
18414         {
18415             isTarget: true,
18416             listeners : listeners || {} 
18417            
18418         
18419         }
18420     );
18421
18422 };
18423
18424 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18425     /**
18426      * @cfg {String} overClass
18427      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18428      */
18429      /**
18430      * @cfg {String} ddGroup
18431      * The drag drop group to handle drop events for
18432      */
18433      
18434     /**
18435      * @cfg {String} dropAllowed
18436      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18437      */
18438     dropAllowed : "x-dd-drop-ok",
18439     /**
18440      * @cfg {String} dropNotAllowed
18441      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18442      */
18443     dropNotAllowed : "x-dd-drop-nodrop",
18444     /**
18445      * @cfg {boolean} success
18446      * set this after drop listener.. 
18447      */
18448     success : false,
18449     /**
18450      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18451      * if the drop point is valid for over/enter..
18452      */
18453     valid : false,
18454     // private
18455     isTarget : true,
18456
18457     // private
18458     isNotifyTarget : true,
18459     
18460     /**
18461      * @hide
18462      */
18463     notifyEnter : function(dd, e, data)
18464     {
18465         this.valid = true;
18466         this.fireEvent('enter', dd, e, data);
18467         if(this.overClass){
18468             this.el.addClass(this.overClass);
18469         }
18470         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18471             this.valid ? this.dropAllowed : this.dropNotAllowed
18472         );
18473     },
18474
18475     /**
18476      * @hide
18477      */
18478     notifyOver : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('over', dd, e, data);
18482         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18483             this.valid ? this.dropAllowed : this.dropNotAllowed
18484         );
18485     },
18486
18487     /**
18488      * @hide
18489      */
18490     notifyOut : function(dd, e, data)
18491     {
18492         this.fireEvent('out', dd, e, data);
18493         if(this.overClass){
18494             this.el.removeClass(this.overClass);
18495         }
18496     },
18497
18498     /**
18499      * @hide
18500      */
18501     notifyDrop : function(dd, e, data)
18502     {
18503         this.success = false;
18504         this.fireEvent('drop', dd, e, data);
18505         return this.success;
18506     }
18507 });/*
18508  * Based on:
18509  * Ext JS Library 1.1.1
18510  * Copyright(c) 2006-2007, Ext JS, LLC.
18511  *
18512  * Originally Released Under LGPL - original licence link has changed is not relivant.
18513  *
18514  * Fork - LGPL
18515  * <script type="text/javascript">
18516  */
18517
18518
18519 /**
18520  * @class Roo.dd.DragZone
18521  * @extends Roo.dd.DragSource
18522  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18523  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18524  * @constructor
18525  * @param {String/HTMLElement/Element} el The container element
18526  * @param {Object} config
18527  */
18528 Roo.dd.DragZone = function(el, config){
18529     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18530     if(this.containerScroll){
18531         Roo.dd.ScrollManager.register(this.el);
18532     }
18533 };
18534
18535 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18536     /**
18537      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18538      * for auto scrolling during drag operations.
18539      */
18540     /**
18541      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18542      * method after a failed drop (defaults to "c3daf9" - light blue)
18543      */
18544
18545     /**
18546      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18547      * for a valid target to drag based on the mouse down. Override this method
18548      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18549      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18550      * @param {EventObject} e The mouse down event
18551      * @return {Object} The dragData
18552      */
18553     getDragData : function(e){
18554         return Roo.dd.Registry.getHandleFromEvent(e);
18555     },
18556     
18557     /**
18558      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18559      * this.dragData.ddel
18560      * @param {Number} x The x position of the click on the dragged object
18561      * @param {Number} y The y position of the click on the dragged object
18562      * @return {Boolean} true to continue the drag, false to cancel
18563      */
18564     onInitDrag : function(x, y){
18565         this.proxy.update(this.dragData.ddel.cloneNode(true));
18566         this.onStartDrag(x, y);
18567         return true;
18568     },
18569     
18570     /**
18571      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18572      */
18573     afterRepair : function(){
18574         if(Roo.enableFx){
18575             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18576         }
18577         this.dragging = false;
18578     },
18579
18580     /**
18581      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18582      * the XY of this.dragData.ddel
18583      * @param {EventObject} e The mouse up event
18584      * @return {Array} The xy location (e.g. [100, 200])
18585      */
18586     getRepairXY : function(e){
18587         return Roo.Element.fly(this.dragData.ddel).getXY();  
18588     }
18589 });/*
18590  * Based on:
18591  * Ext JS Library 1.1.1
18592  * Copyright(c) 2006-2007, Ext JS, LLC.
18593  *
18594  * Originally Released Under LGPL - original licence link has changed is not relivant.
18595  *
18596  * Fork - LGPL
18597  * <script type="text/javascript">
18598  */
18599 /**
18600  * @class Roo.dd.DropZone
18601  * @extends Roo.dd.DropTarget
18602  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18603  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18604  * @constructor
18605  * @param {String/HTMLElement/Element} el The container element
18606  * @param {Object} config
18607  */
18608 Roo.dd.DropZone = function(el, config){
18609     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18610 };
18611
18612 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18613     /**
18614      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18615      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18616      * provide your own custom lookup.
18617      * @param {Event} e The event
18618      * @return {Object} data The custom data
18619      */
18620     getTargetFromEvent : function(e){
18621         return Roo.dd.Registry.getTargetFromEvent(e);
18622     },
18623
18624     /**
18625      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18626      * that it has registered.  This method has no default implementation and should be overridden to provide
18627      * node-specific processing if necessary.
18628      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18629      * {@link #getTargetFromEvent} for this node)
18630      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18631      * @param {Event} e The event
18632      * @param {Object} data An object containing arbitrary data supplied by the drag source
18633      */
18634     onNodeEnter : function(n, dd, e, data){
18635         
18636     },
18637
18638     /**
18639      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18640      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18641      * overridden to provide the proper feedback.
18642      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18643      * {@link #getTargetFromEvent} for this node)
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18648      * underlying {@link Roo.dd.StatusProxy} can be updated
18649      */
18650     onNodeOver : function(n, dd, e, data){
18651         return this.dropAllowed;
18652     },
18653
18654     /**
18655      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18656      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18657      * node-specific processing if necessary.
18658      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18659      * {@link #getTargetFromEvent} for this node)
18660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18661      * @param {Event} e The event
18662      * @param {Object} data An object containing arbitrary data supplied by the drag source
18663      */
18664     onNodeOut : function(n, dd, e, data){
18665         
18666     },
18667
18668     /**
18669      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18670      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18671      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18672      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18673      * {@link #getTargetFromEvent} for this node)
18674      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18675      * @param {Event} e The event
18676      * @param {Object} data An object containing arbitrary data supplied by the drag source
18677      * @return {Boolean} True if the drop was valid, else false
18678      */
18679     onNodeDrop : function(n, dd, e, data){
18680         return false;
18681     },
18682
18683     /**
18684      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18685      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18686      * it should be overridden to provide the proper feedback if necessary.
18687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18688      * @param {Event} e The event
18689      * @param {Object} data An object containing arbitrary data supplied by the drag source
18690      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18691      * underlying {@link Roo.dd.StatusProxy} can be updated
18692      */
18693     onContainerOver : function(dd, e, data){
18694         return this.dropNotAllowed;
18695     },
18696
18697     /**
18698      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18699      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18700      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18701      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
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 {Boolean} True if the drop was valid, else false
18706      */
18707     onContainerDrop : function(dd, e, data){
18708         return false;
18709     },
18710
18711     /**
18712      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18713      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18714      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18715      * you should override this method and provide a custom implementation.
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18720      * underlying {@link Roo.dd.StatusProxy} can be updated
18721      */
18722     notifyEnter : function(dd, e, data){
18723         return this.dropNotAllowed;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18728      * This method will be called on every mouse movement while the drag source is over the drop zone.
18729      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18730      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18731      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18732      * registered node, it will call {@link #onContainerOver}.
18733      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18734      * @param {Event} e The event
18735      * @param {Object} data An object containing arbitrary data supplied by the drag source
18736      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18737      * underlying {@link Roo.dd.StatusProxy} can be updated
18738      */
18739     notifyOver : function(dd, e, data){
18740         var n = this.getTargetFromEvent(e);
18741         if(!n){ // not over valid drop target
18742             if(this.lastOverNode){
18743                 this.onNodeOut(this.lastOverNode, dd, e, data);
18744                 this.lastOverNode = null;
18745             }
18746             return this.onContainerOver(dd, e, data);
18747         }
18748         if(this.lastOverNode != n){
18749             if(this.lastOverNode){
18750                 this.onNodeOut(this.lastOverNode, dd, e, data);
18751             }
18752             this.onNodeEnter(n, dd, e, data);
18753             this.lastOverNode = n;
18754         }
18755         return this.onNodeOver(n, dd, e, data);
18756     },
18757
18758     /**
18759      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18760      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18761      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18762      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18763      * @param {Event} e The event
18764      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18765      */
18766     notifyOut : function(dd, e, data){
18767         if(this.lastOverNode){
18768             this.onNodeOut(this.lastOverNode, dd, e, data);
18769             this.lastOverNode = null;
18770         }
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18775      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18776      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18777      * otherwise it will call {@link #onContainerDrop}.
18778      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18779      * @param {Event} e The event
18780      * @param {Object} data An object containing arbitrary data supplied by the drag source
18781      * @return {Boolean} True if the drop was valid, else false
18782      */
18783     notifyDrop : function(dd, e, data){
18784         if(this.lastOverNode){
18785             this.onNodeOut(this.lastOverNode, dd, e, data);
18786             this.lastOverNode = null;
18787         }
18788         var n = this.getTargetFromEvent(e);
18789         return n ?
18790             this.onNodeDrop(n, dd, e, data) :
18791             this.onContainerDrop(dd, e, data);
18792     },
18793
18794     // private
18795     triggerCacheRefresh : function(){
18796         Roo.dd.DDM.refreshCache(this.groups);
18797     }  
18798 });/*
18799  * Based on:
18800  * Ext JS Library 1.1.1
18801  * Copyright(c) 2006-2007, Ext JS, LLC.
18802  *
18803  * Originally Released Under LGPL - original licence link has changed is not relivant.
18804  *
18805  * Fork - LGPL
18806  * <script type="text/javascript">
18807  */
18808
18809
18810 /**
18811  * @class Roo.data.SortTypes
18812  * @singleton
18813  * Defines the default sorting (casting?) comparison functions used when sorting data.
18814  */
18815 Roo.data.SortTypes = {
18816     /**
18817      * Default sort that does nothing
18818      * @param {Mixed} s The value being converted
18819      * @return {Mixed} The comparison value
18820      */
18821     none : function(s){
18822         return s;
18823     },
18824     
18825     /**
18826      * The regular expression used to strip tags
18827      * @type {RegExp}
18828      * @property
18829      */
18830     stripTagsRE : /<\/?[^>]+>/gi,
18831     
18832     /**
18833      * Strips all HTML tags to sort on text only
18834      * @param {Mixed} s The value being converted
18835      * @return {String} The comparison value
18836      */
18837     asText : function(s){
18838         return String(s).replace(this.stripTagsRE, "");
18839     },
18840     
18841     /**
18842      * Strips all HTML tags to sort on text only - Case insensitive
18843      * @param {Mixed} s The value being converted
18844      * @return {String} The comparison value
18845      */
18846     asUCText : function(s){
18847         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18848     },
18849     
18850     /**
18851      * Case insensitive string
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asUCString : function(s) {
18856         return String(s).toUpperCase();
18857     },
18858     
18859     /**
18860      * Date sorting
18861      * @param {Mixed} s The value being converted
18862      * @return {Number} The comparison value
18863      */
18864     asDate : function(s) {
18865         if(!s){
18866             return 0;
18867         }
18868         if(s instanceof Date){
18869             return s.getTime();
18870         }
18871         return Date.parse(String(s));
18872     },
18873     
18874     /**
18875      * Float sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Float} The comparison value
18878      */
18879     asFloat : function(s) {
18880         var val = parseFloat(String(s).replace(/,/g, ""));
18881         if(isNaN(val)) val = 0;
18882         return val;
18883     },
18884     
18885     /**
18886      * Integer sorting
18887      * @param {Mixed} s The value being converted
18888      * @return {Number} The comparison value
18889      */
18890     asInt : function(s) {
18891         var val = parseInt(String(s).replace(/,/g, ""));
18892         if(isNaN(val)) val = 0;
18893         return val;
18894     }
18895 };/*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905
18906 /**
18907 * @class Roo.data.Record
18908  * Instances of this class encapsulate both record <em>definition</em> information, and record
18909  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18910  * to access Records cached in an {@link Roo.data.Store} object.<br>
18911  * <p>
18912  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18913  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18914  * objects.<br>
18915  * <p>
18916  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18917  * @constructor
18918  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18919  * {@link #create}. The parameters are the same.
18920  * @param {Array} data An associative Array of data values keyed by the field name.
18921  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18922  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18923  * not specified an integer id is generated.
18924  */
18925 Roo.data.Record = function(data, id){
18926     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18927     this.data = data;
18928 };
18929
18930 /**
18931  * Generate a constructor for a specific record layout.
18932  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18933  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18934  * Each field definition object may contain the following properties: <ul>
18935  * <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,
18936  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18937  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18938  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18939  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18940  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18941  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18942  * this may be omitted.</p></li>
18943  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18944  * <ul><li>auto (Default, implies no conversion)</li>
18945  * <li>string</li>
18946  * <li>int</li>
18947  * <li>float</li>
18948  * <li>boolean</li>
18949  * <li>date</li></ul></p></li>
18950  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18951  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18952  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18953  * by the Reader into an object that will be stored in the Record. It is passed the
18954  * following parameters:<ul>
18955  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18956  * </ul></p></li>
18957  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18958  * </ul>
18959  * <br>usage:<br><pre><code>
18960 var TopicRecord = Roo.data.Record.create(
18961     {name: 'title', mapping: 'topic_title'},
18962     {name: 'author', mapping: 'username'},
18963     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18964     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18965     {name: 'lastPoster', mapping: 'user2'},
18966     {name: 'excerpt', mapping: 'post_text'}
18967 );
18968
18969 var myNewRecord = new TopicRecord({
18970     title: 'Do my job please',
18971     author: 'noobie',
18972     totalPosts: 1,
18973     lastPost: new Date(),
18974     lastPoster: 'Animal',
18975     excerpt: 'No way dude!'
18976 });
18977 myStore.add(myNewRecord);
18978 </code></pre>
18979  * @method create
18980  * @static
18981  */
18982 Roo.data.Record.create = function(o){
18983     var f = function(){
18984         f.superclass.constructor.apply(this, arguments);
18985     };
18986     Roo.extend(f, Roo.data.Record);
18987     var p = f.prototype;
18988     p.fields = new Roo.util.MixedCollection(false, function(field){
18989         return field.name;
18990     });
18991     for(var i = 0, len = o.length; i < len; i++){
18992         p.fields.add(new Roo.data.Field(o[i]));
18993     }
18994     f.getField = function(name){
18995         return p.fields.get(name);  
18996     };
18997     return f;
18998 };
18999
19000 Roo.data.Record.AUTO_ID = 1000;
19001 Roo.data.Record.EDIT = 'edit';
19002 Roo.data.Record.REJECT = 'reject';
19003 Roo.data.Record.COMMIT = 'commit';
19004
19005 Roo.data.Record.prototype = {
19006     /**
19007      * Readonly flag - true if this record has been modified.
19008      * @type Boolean
19009      */
19010     dirty : false,
19011     editing : false,
19012     error: null,
19013     modified: null,
19014
19015     // private
19016     join : function(store){
19017         this.store = store;
19018     },
19019
19020     /**
19021      * Set the named field to the specified value.
19022      * @param {String} name The name of the field to set.
19023      * @param {Object} value The value to set the field to.
19024      */
19025     set : function(name, value){
19026         if(this.data[name] == value){
19027             return;
19028         }
19029         this.dirty = true;
19030         if(!this.modified){
19031             this.modified = {};
19032         }
19033         if(typeof this.modified[name] == 'undefined'){
19034             this.modified[name] = this.data[name];
19035         }
19036         this.data[name] = value;
19037         if(!this.editing){
19038             this.store.afterEdit(this);
19039         }       
19040     },
19041
19042     /**
19043      * Get the value of the named field.
19044      * @param {String} name The name of the field to get the value of.
19045      * @return {Object} The value of the field.
19046      */
19047     get : function(name){
19048         return this.data[name]; 
19049     },
19050
19051     // private
19052     beginEdit : function(){
19053         this.editing = true;
19054         this.modified = {}; 
19055     },
19056
19057     // private
19058     cancelEdit : function(){
19059         this.editing = false;
19060         delete this.modified;
19061     },
19062
19063     // private
19064     endEdit : function(){
19065         this.editing = false;
19066         if(this.dirty && this.store){
19067             this.store.afterEdit(this);
19068         }
19069     },
19070
19071     /**
19072      * Usually called by the {@link Roo.data.Store} which owns the Record.
19073      * Rejects all changes made to the Record since either creation, or the last commit operation.
19074      * Modified fields are reverted to their original values.
19075      * <p>
19076      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19077      * of reject operations.
19078      */
19079     reject : function(){
19080         var m = this.modified;
19081         for(var n in m){
19082             if(typeof m[n] != "function"){
19083                 this.data[n] = m[n];
19084             }
19085         }
19086         this.dirty = false;
19087         delete this.modified;
19088         this.editing = false;
19089         if(this.store){
19090             this.store.afterReject(this);
19091         }
19092     },
19093
19094     /**
19095      * Usually called by the {@link Roo.data.Store} which owns the Record.
19096      * Commits all changes made to the Record since either creation, or the last commit operation.
19097      * <p>
19098      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19099      * of commit operations.
19100      */
19101     commit : function(){
19102         this.dirty = false;
19103         delete this.modified;
19104         this.editing = false;
19105         if(this.store){
19106             this.store.afterCommit(this);
19107         }
19108     },
19109
19110     // private
19111     hasError : function(){
19112         return this.error != null;
19113     },
19114
19115     // private
19116     clearError : function(){
19117         this.error = null;
19118     },
19119
19120     /**
19121      * Creates a copy of this record.
19122      * @param {String} id (optional) A new record id if you don't want to use this record's id
19123      * @return {Record}
19124      */
19125     copy : function(newId) {
19126         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19127     }
19128 };/*
19129  * Based on:
19130  * Ext JS Library 1.1.1
19131  * Copyright(c) 2006-2007, Ext JS, LLC.
19132  *
19133  * Originally Released Under LGPL - original licence link has changed is not relivant.
19134  *
19135  * Fork - LGPL
19136  * <script type="text/javascript">
19137  */
19138
19139
19140
19141 /**
19142  * @class Roo.data.Store
19143  * @extends Roo.util.Observable
19144  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19145  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19146  * <p>
19147  * 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
19148  * has no knowledge of the format of the data returned by the Proxy.<br>
19149  * <p>
19150  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19151  * instances from the data object. These records are cached and made available through accessor functions.
19152  * @constructor
19153  * Creates a new Store.
19154  * @param {Object} config A config object containing the objects needed for the Store to access data,
19155  * and read the data into Records.
19156  */
19157 Roo.data.Store = function(config){
19158     this.data = new Roo.util.MixedCollection(false);
19159     this.data.getKey = function(o){
19160         return o.id;
19161     };
19162     this.baseParams = {};
19163     // private
19164     this.paramNames = {
19165         "start" : "start",
19166         "limit" : "limit",
19167         "sort" : "sort",
19168         "dir" : "dir",
19169         "multisort" : "_multisort"
19170     };
19171
19172     if(config && config.data){
19173         this.inlineData = config.data;
19174         delete config.data;
19175     }
19176
19177     Roo.apply(this, config);
19178     
19179     if(this.reader){ // reader passed
19180         this.reader = Roo.factory(this.reader, Roo.data);
19181         this.reader.xmodule = this.xmodule || false;
19182         if(!this.recordType){
19183             this.recordType = this.reader.recordType;
19184         }
19185         if(this.reader.onMetaChange){
19186             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19187         }
19188     }
19189
19190     if(this.recordType){
19191         this.fields = this.recordType.prototype.fields;
19192     }
19193     this.modified = [];
19194
19195     this.addEvents({
19196         /**
19197          * @event datachanged
19198          * Fires when the data cache has changed, and a widget which is using this Store
19199          * as a Record cache should refresh its view.
19200          * @param {Store} this
19201          */
19202         datachanged : true,
19203         /**
19204          * @event metachange
19205          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19206          * @param {Store} this
19207          * @param {Object} meta The JSON metadata
19208          */
19209         metachange : true,
19210         /**
19211          * @event add
19212          * Fires when Records have been added to the Store
19213          * @param {Store} this
19214          * @param {Roo.data.Record[]} records The array of Records added
19215          * @param {Number} index The index at which the record(s) were added
19216          */
19217         add : true,
19218         /**
19219          * @event remove
19220          * Fires when a Record has been removed from the Store
19221          * @param {Store} this
19222          * @param {Roo.data.Record} record The Record that was removed
19223          * @param {Number} index The index at which the record was removed
19224          */
19225         remove : true,
19226         /**
19227          * @event update
19228          * Fires when a Record has been updated
19229          * @param {Store} this
19230          * @param {Roo.data.Record} record The Record that was updated
19231          * @param {String} operation The update operation being performed.  Value may be one of:
19232          * <pre><code>
19233  Roo.data.Record.EDIT
19234  Roo.data.Record.REJECT
19235  Roo.data.Record.COMMIT
19236          * </code></pre>
19237          */
19238         update : true,
19239         /**
19240          * @event clear
19241          * Fires when the data cache has been cleared.
19242          * @param {Store} this
19243          */
19244         clear : true,
19245         /**
19246          * @event beforeload
19247          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19248          * the load action will be canceled.
19249          * @param {Store} this
19250          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19251          */
19252         beforeload : true,
19253         /**
19254          * @event load
19255          * Fires after a new set of Records has been loaded.
19256          * @param {Store} this
19257          * @param {Roo.data.Record[]} records The Records that were loaded
19258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19259          */
19260         load : true,
19261         /**
19262          * @event loadexception
19263          * Fires if an exception occurs in the Proxy during loading.
19264          * Called with the signature of the Proxy's "loadexception" event.
19265          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19266          * 
19267          * @param {Proxy} 
19268          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19269          * @param {Object} load options 
19270          * @param {Object} jsonData from your request (normally this contains the Exception)
19271          */
19272         loadexception : true
19273     });
19274     
19275     if(this.proxy){
19276         this.proxy = Roo.factory(this.proxy, Roo.data);
19277         this.proxy.xmodule = this.xmodule || false;
19278         this.relayEvents(this.proxy,  ["loadexception"]);
19279     }
19280     this.sortToggle = {};
19281     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19282
19283     Roo.data.Store.superclass.constructor.call(this);
19284
19285     if(this.inlineData){
19286         this.loadData(this.inlineData);
19287         delete this.inlineData;
19288     }
19289 };
19290 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19291      /**
19292     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19293     * without a remote query - used by combo/forms at present.
19294     */
19295     
19296     /**
19297     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19298     */
19299     /**
19300     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19301     */
19302     /**
19303     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19304     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19305     */
19306     /**
19307     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19308     * on any HTTP request
19309     */
19310     /**
19311     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19312     */
19313     /**
19314     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19315     */
19316     multiSort: false,
19317     /**
19318     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19319     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19320     */
19321     remoteSort : false,
19322
19323     /**
19324     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19325      * loaded or when a record is removed. (defaults to false).
19326     */
19327     pruneModifiedRecords : false,
19328
19329     // private
19330     lastOptions : null,
19331
19332     /**
19333      * Add Records to the Store and fires the add event.
19334      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19335      */
19336     add : function(records){
19337         records = [].concat(records);
19338         for(var i = 0, len = records.length; i < len; i++){
19339             records[i].join(this);
19340         }
19341         var index = this.data.length;
19342         this.data.addAll(records);
19343         this.fireEvent("add", this, records, index);
19344     },
19345
19346     /**
19347      * Remove a Record from the Store and fires the remove event.
19348      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19349      */
19350     remove : function(record){
19351         var index = this.data.indexOf(record);
19352         this.data.removeAt(index);
19353         if(this.pruneModifiedRecords){
19354             this.modified.remove(record);
19355         }
19356         this.fireEvent("remove", this, record, index);
19357     },
19358
19359     /**
19360      * Remove all Records from the Store and fires the clear event.
19361      */
19362     removeAll : function(){
19363         this.data.clear();
19364         if(this.pruneModifiedRecords){
19365             this.modified = [];
19366         }
19367         this.fireEvent("clear", this);
19368     },
19369
19370     /**
19371      * Inserts Records to the Store at the given index and fires the add event.
19372      * @param {Number} index The start index at which to insert the passed Records.
19373      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19374      */
19375     insert : function(index, records){
19376         records = [].concat(records);
19377         for(var i = 0, len = records.length; i < len; i++){
19378             this.data.insert(index, records[i]);
19379             records[i].join(this);
19380         }
19381         this.fireEvent("add", this, records, index);
19382     },
19383
19384     /**
19385      * Get the index within the cache of the passed Record.
19386      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19387      * @return {Number} The index of the passed Record. Returns -1 if not found.
19388      */
19389     indexOf : function(record){
19390         return this.data.indexOf(record);
19391     },
19392
19393     /**
19394      * Get the index within the cache of the Record with the passed id.
19395      * @param {String} id The id of the Record to find.
19396      * @return {Number} The index of the Record. Returns -1 if not found.
19397      */
19398     indexOfId : function(id){
19399         return this.data.indexOfKey(id);
19400     },
19401
19402     /**
19403      * Get the Record with the specified id.
19404      * @param {String} id The id of the Record to find.
19405      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19406      */
19407     getById : function(id){
19408         return this.data.key(id);
19409     },
19410
19411     /**
19412      * Get the Record at the specified index.
19413      * @param {Number} index The index of the Record to find.
19414      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19415      */
19416     getAt : function(index){
19417         return this.data.itemAt(index);
19418     },
19419
19420     /**
19421      * Returns a range of Records between specified indices.
19422      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19423      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19424      * @return {Roo.data.Record[]} An array of Records
19425      */
19426     getRange : function(start, end){
19427         return this.data.getRange(start, end);
19428     },
19429
19430     // private
19431     storeOptions : function(o){
19432         o = Roo.apply({}, o);
19433         delete o.callback;
19434         delete o.scope;
19435         this.lastOptions = o;
19436     },
19437
19438     /**
19439      * Loads the Record cache from the configured Proxy using the configured Reader.
19440      * <p>
19441      * If using remote paging, then the first load call must specify the <em>start</em>
19442      * and <em>limit</em> properties in the options.params property to establish the initial
19443      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19444      * <p>
19445      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19446      * and this call will return before the new data has been loaded. Perform any post-processing
19447      * in a callback function, or in a "load" event handler.</strong>
19448      * <p>
19449      * @param {Object} options An object containing properties which control loading options:<ul>
19450      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19451      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19452      * passed the following arguments:<ul>
19453      * <li>r : Roo.data.Record[]</li>
19454      * <li>options: Options object from the load call</li>
19455      * <li>success: Boolean success indicator</li></ul></li>
19456      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19457      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19458      * </ul>
19459      */
19460     load : function(options){
19461         options = options || {};
19462         if(this.fireEvent("beforeload", this, options) !== false){
19463             this.storeOptions(options);
19464             var p = Roo.apply(options.params || {}, this.baseParams);
19465             // if meta was not loaded from remote source.. try requesting it.
19466             if (!this.reader.metaFromRemote) {
19467                 p._requestMeta = 1;
19468             }
19469             if(this.sortInfo && this.remoteSort){
19470                 var pn = this.paramNames;
19471                 p[pn["sort"]] = this.sortInfo.field;
19472                 p[pn["dir"]] = this.sortInfo.direction;
19473             }
19474             if (this.multiSort) {
19475                 var pn = this.paramNames;
19476                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19477             }
19478             
19479             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19480         }
19481     },
19482
19483     /**
19484      * Reloads the Record cache from the configured Proxy using the configured Reader and
19485      * the options from the last load operation performed.
19486      * @param {Object} options (optional) An object containing properties which may override the options
19487      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19488      * the most recently used options are reused).
19489      */
19490     reload : function(options){
19491         this.load(Roo.applyIf(options||{}, this.lastOptions));
19492     },
19493
19494     // private
19495     // Called as a callback by the Reader during a load operation.
19496     loadRecords : function(o, options, success){
19497         if(!o || success === false){
19498             if(success !== false){
19499                 this.fireEvent("load", this, [], options);
19500             }
19501             if(options.callback){
19502                 options.callback.call(options.scope || this, [], options, false);
19503             }
19504             return;
19505         }
19506         // if data returned failure - throw an exception.
19507         if (o.success === false) {
19508             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19509             return;
19510         }
19511         var r = o.records, t = o.totalRecords || r.length;
19512         if(!options || options.add !== true){
19513             if(this.pruneModifiedRecords){
19514                 this.modified = [];
19515             }
19516             for(var i = 0, len = r.length; i < len; i++){
19517                 r[i].join(this);
19518             }
19519             if(this.snapshot){
19520                 this.data = this.snapshot;
19521                 delete this.snapshot;
19522             }
19523             this.data.clear();
19524             this.data.addAll(r);
19525             this.totalLength = t;
19526             this.applySort();
19527             this.fireEvent("datachanged", this);
19528         }else{
19529             this.totalLength = Math.max(t, this.data.length+r.length);
19530             this.add(r);
19531         }
19532         this.fireEvent("load", this, r, options);
19533         if(options.callback){
19534             options.callback.call(options.scope || this, r, options, true);
19535         }
19536     },
19537
19538     /**
19539      * Loads data from a passed data block. A Reader which understands the format of the data
19540      * must have been configured in the constructor.
19541      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19542      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19543      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19544      */
19545     loadData : function(o, append){
19546         var r = this.reader.readRecords(o);
19547         this.loadRecords(r, {add: append}, true);
19548     },
19549
19550     /**
19551      * Gets the number of cached records.
19552      * <p>
19553      * <em>If using paging, this may not be the total size of the dataset. If the data object
19554      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19555      * the data set size</em>
19556      */
19557     getCount : function(){
19558         return this.data.length || 0;
19559     },
19560
19561     /**
19562      * Gets the total number of records in the dataset as returned by the server.
19563      * <p>
19564      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19565      * the dataset size</em>
19566      */
19567     getTotalCount : function(){
19568         return this.totalLength || 0;
19569     },
19570
19571     /**
19572      * Returns the sort state of the Store as an object with two properties:
19573      * <pre><code>
19574  field {String} The name of the field by which the Records are sorted
19575  direction {String} The sort order, "ASC" or "DESC"
19576      * </code></pre>
19577      */
19578     getSortState : function(){
19579         return this.sortInfo;
19580     },
19581
19582     // private
19583     applySort : function(){
19584         if(this.sortInfo && !this.remoteSort){
19585             var s = this.sortInfo, f = s.field;
19586             var st = this.fields.get(f).sortType;
19587             var fn = function(r1, r2){
19588                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19589                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19590             };
19591             this.data.sort(s.direction, fn);
19592             if(this.snapshot && this.snapshot != this.data){
19593                 this.snapshot.sort(s.direction, fn);
19594             }
19595         }
19596     },
19597
19598     /**
19599      * Sets the default sort column and order to be used by the next load operation.
19600      * @param {String} fieldName The name of the field to sort by.
19601      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19602      */
19603     setDefaultSort : function(field, dir){
19604         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19605     },
19606
19607     /**
19608      * Sort the Records.
19609      * If remote sorting is used, the sort is performed on the server, and the cache is
19610      * reloaded. If local sorting is used, the cache is sorted internally.
19611      * @param {String} fieldName The name of the field to sort by.
19612      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19613      */
19614     sort : function(fieldName, dir){
19615         var f = this.fields.get(fieldName);
19616         if(!dir){
19617             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19618             
19619             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19620                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19621             }else{
19622                 dir = f.sortDir;
19623             }
19624         }
19625         this.sortToggle[f.name] = dir;
19626         this.sortInfo = {field: f.name, direction: dir};
19627         if(!this.remoteSort){
19628             this.applySort();
19629             this.fireEvent("datachanged", this);
19630         }else{
19631             this.load(this.lastOptions);
19632         }
19633     },
19634
19635     /**
19636      * Calls the specified function for each of the Records in the cache.
19637      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19638      * Returning <em>false</em> aborts and exits the iteration.
19639      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19640      */
19641     each : function(fn, scope){
19642         this.data.each(fn, scope);
19643     },
19644
19645     /**
19646      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19647      * (e.g., during paging).
19648      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19649      */
19650     getModifiedRecords : function(){
19651         return this.modified;
19652     },
19653
19654     // private
19655     createFilterFn : function(property, value, anyMatch){
19656         if(!value.exec){ // not a regex
19657             value = String(value);
19658             if(value.length == 0){
19659                 return false;
19660             }
19661             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19662         }
19663         return function(r){
19664             return value.test(r.data[property]);
19665         };
19666     },
19667
19668     /**
19669      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19670      * @param {String} property A field on your records
19671      * @param {Number} start The record index to start at (defaults to 0)
19672      * @param {Number} end The last record index to include (defaults to length - 1)
19673      * @return {Number} The sum
19674      */
19675     sum : function(property, start, end){
19676         var rs = this.data.items, v = 0;
19677         start = start || 0;
19678         end = (end || end === 0) ? end : rs.length-1;
19679
19680         for(var i = start; i <= end; i++){
19681             v += (rs[i].data[property] || 0);
19682         }
19683         return v;
19684     },
19685
19686     /**
19687      * Filter the records by a specified property.
19688      * @param {String} field A field on your records
19689      * @param {String/RegExp} value Either a string that the field
19690      * should start with or a RegExp to test against the field
19691      * @param {Boolean} anyMatch True to match any part not just the beginning
19692      */
19693     filter : function(property, value, anyMatch){
19694         var fn = this.createFilterFn(property, value, anyMatch);
19695         return fn ? this.filterBy(fn) : this.clearFilter();
19696     },
19697
19698     /**
19699      * Filter by a function. The specified function will be called with each
19700      * record in this data source. If the function returns true the record is included,
19701      * otherwise it is filtered.
19702      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19703      * @param {Object} scope (optional) The scope of the function (defaults to this)
19704      */
19705     filterBy : function(fn, scope){
19706         this.snapshot = this.snapshot || this.data;
19707         this.data = this.queryBy(fn, scope||this);
19708         this.fireEvent("datachanged", this);
19709     },
19710
19711     /**
19712      * Query the records by a specified property.
19713      * @param {String} field A field on your records
19714      * @param {String/RegExp} value Either a string that the field
19715      * should start with or a RegExp to test against the field
19716      * @param {Boolean} anyMatch True to match any part not just the beginning
19717      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19718      */
19719     query : function(property, value, anyMatch){
19720         var fn = this.createFilterFn(property, value, anyMatch);
19721         return fn ? this.queryBy(fn) : this.data.clone();
19722     },
19723
19724     /**
19725      * Query by a function. The specified function will be called with each
19726      * record in this data source. If the function returns true the record is included
19727      * in the results.
19728      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19729      * @param {Object} scope (optional) The scope of the function (defaults to this)
19730       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19731      **/
19732     queryBy : function(fn, scope){
19733         var data = this.snapshot || this.data;
19734         return data.filterBy(fn, scope||this);
19735     },
19736
19737     /**
19738      * Collects unique values for a particular dataIndex from this store.
19739      * @param {String} dataIndex The property to collect
19740      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19741      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19742      * @return {Array} An array of the unique values
19743      **/
19744     collect : function(dataIndex, allowNull, bypassFilter){
19745         var d = (bypassFilter === true && this.snapshot) ?
19746                 this.snapshot.items : this.data.items;
19747         var v, sv, r = [], l = {};
19748         for(var i = 0, len = d.length; i < len; i++){
19749             v = d[i].data[dataIndex];
19750             sv = String(v);
19751             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19752                 l[sv] = true;
19753                 r[r.length] = v;
19754             }
19755         }
19756         return r;
19757     },
19758
19759     /**
19760      * Revert to a view of the Record cache with no filtering applied.
19761      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19762      */
19763     clearFilter : function(suppressEvent){
19764         if(this.snapshot && this.snapshot != this.data){
19765             this.data = this.snapshot;
19766             delete this.snapshot;
19767             if(suppressEvent !== true){
19768                 this.fireEvent("datachanged", this);
19769             }
19770         }
19771     },
19772
19773     // private
19774     afterEdit : function(record){
19775         if(this.modified.indexOf(record) == -1){
19776             this.modified.push(record);
19777         }
19778         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19779     },
19780
19781     // private
19782     afterReject : function(record){
19783         this.modified.remove(record);
19784         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19785     },
19786
19787     // private
19788     afterCommit : function(record){
19789         this.modified.remove(record);
19790         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19791     },
19792
19793     /**
19794      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19795      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19796      */
19797     commitChanges : function(){
19798         var m = this.modified.slice(0);
19799         this.modified = [];
19800         for(var i = 0, len = m.length; i < len; i++){
19801             m[i].commit();
19802         }
19803     },
19804
19805     /**
19806      * Cancel outstanding changes on all changed records.
19807      */
19808     rejectChanges : function(){
19809         var m = this.modified.slice(0);
19810         this.modified = [];
19811         for(var i = 0, len = m.length; i < len; i++){
19812             m[i].reject();
19813         }
19814     },
19815
19816     onMetaChange : function(meta, rtype, o){
19817         this.recordType = rtype;
19818         this.fields = rtype.prototype.fields;
19819         delete this.snapshot;
19820         this.sortInfo = meta.sortInfo || this.sortInfo;
19821         this.modified = [];
19822         this.fireEvent('metachange', this, this.reader.meta);
19823     }
19824 });/*
19825  * Based on:
19826  * Ext JS Library 1.1.1
19827  * Copyright(c) 2006-2007, Ext JS, LLC.
19828  *
19829  * Originally Released Under LGPL - original licence link has changed is not relivant.
19830  *
19831  * Fork - LGPL
19832  * <script type="text/javascript">
19833  */
19834
19835 /**
19836  * @class Roo.data.SimpleStore
19837  * @extends Roo.data.Store
19838  * Small helper class to make creating Stores from Array data easier.
19839  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19840  * @cfg {Array} fields An array of field definition objects, or field name strings.
19841  * @cfg {Array} data The multi-dimensional array of data
19842  * @constructor
19843  * @param {Object} config
19844  */
19845 Roo.data.SimpleStore = function(config){
19846     Roo.data.SimpleStore.superclass.constructor.call(this, {
19847         isLocal : true,
19848         reader: new Roo.data.ArrayReader({
19849                 id: config.id
19850             },
19851             Roo.data.Record.create(config.fields)
19852         ),
19853         proxy : new Roo.data.MemoryProxy(config.data)
19854     });
19855     this.load();
19856 };
19857 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19858  * Based on:
19859  * Ext JS Library 1.1.1
19860  * Copyright(c) 2006-2007, Ext JS, LLC.
19861  *
19862  * Originally Released Under LGPL - original licence link has changed is not relivant.
19863  *
19864  * Fork - LGPL
19865  * <script type="text/javascript">
19866  */
19867
19868 /**
19869 /**
19870  * @extends Roo.data.Store
19871  * @class Roo.data.JsonStore
19872  * Small helper class to make creating Stores for JSON data easier. <br/>
19873 <pre><code>
19874 var store = new Roo.data.JsonStore({
19875     url: 'get-images.php',
19876     root: 'images',
19877     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19878 });
19879 </code></pre>
19880  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19881  * JsonReader and HttpProxy (unless inline data is provided).</b>
19882  * @cfg {Array} fields An array of field definition objects, or field name strings.
19883  * @constructor
19884  * @param {Object} config
19885  */
19886 Roo.data.JsonStore = function(c){
19887     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19888         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19889         reader: new Roo.data.JsonReader(c, c.fields)
19890     }));
19891 };
19892 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19893  * Based on:
19894  * Ext JS Library 1.1.1
19895  * Copyright(c) 2006-2007, Ext JS, LLC.
19896  *
19897  * Originally Released Under LGPL - original licence link has changed is not relivant.
19898  *
19899  * Fork - LGPL
19900  * <script type="text/javascript">
19901  */
19902
19903  
19904 Roo.data.Field = function(config){
19905     if(typeof config == "string"){
19906         config = {name: config};
19907     }
19908     Roo.apply(this, config);
19909     
19910     if(!this.type){
19911         this.type = "auto";
19912     }
19913     
19914     var st = Roo.data.SortTypes;
19915     // named sortTypes are supported, here we look them up
19916     if(typeof this.sortType == "string"){
19917         this.sortType = st[this.sortType];
19918     }
19919     
19920     // set default sortType for strings and dates
19921     if(!this.sortType){
19922         switch(this.type){
19923             case "string":
19924                 this.sortType = st.asUCString;
19925                 break;
19926             case "date":
19927                 this.sortType = st.asDate;
19928                 break;
19929             default:
19930                 this.sortType = st.none;
19931         }
19932     }
19933
19934     // define once
19935     var stripRe = /[\$,%]/g;
19936
19937     // prebuilt conversion function for this field, instead of
19938     // switching every time we're reading a value
19939     if(!this.convert){
19940         var cv, dateFormat = this.dateFormat;
19941         switch(this.type){
19942             case "":
19943             case "auto":
19944             case undefined:
19945                 cv = function(v){ return v; };
19946                 break;
19947             case "string":
19948                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19949                 break;
19950             case "int":
19951                 cv = function(v){
19952                     return v !== undefined && v !== null && v !== '' ?
19953                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19954                     };
19955                 break;
19956             case "float":
19957                 cv = function(v){
19958                     return v !== undefined && v !== null && v !== '' ?
19959                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19960                     };
19961                 break;
19962             case "bool":
19963             case "boolean":
19964                 cv = function(v){ return v === true || v === "true" || v == 1; };
19965                 break;
19966             case "date":
19967                 cv = function(v){
19968                     if(!v){
19969                         return '';
19970                     }
19971                     if(v instanceof Date){
19972                         return v;
19973                     }
19974                     if(dateFormat){
19975                         if(dateFormat == "timestamp"){
19976                             return new Date(v*1000);
19977                         }
19978                         return Date.parseDate(v, dateFormat);
19979                     }
19980                     var parsed = Date.parse(v);
19981                     return parsed ? new Date(parsed) : null;
19982                 };
19983              break;
19984             
19985         }
19986         this.convert = cv;
19987     }
19988 };
19989
19990 Roo.data.Field.prototype = {
19991     dateFormat: null,
19992     defaultValue: "",
19993     mapping: null,
19994     sortType : null,
19995     sortDir : "ASC"
19996 };/*
19997  * Based on:
19998  * Ext JS Library 1.1.1
19999  * Copyright(c) 2006-2007, Ext JS, LLC.
20000  *
20001  * Originally Released Under LGPL - original licence link has changed is not relivant.
20002  *
20003  * Fork - LGPL
20004  * <script type="text/javascript">
20005  */
20006  
20007 // Base class for reading structured data from a data source.  This class is intended to be
20008 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20009
20010 /**
20011  * @class Roo.data.DataReader
20012  * Base class for reading structured data from a data source.  This class is intended to be
20013  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20014  */
20015
20016 Roo.data.DataReader = function(meta, recordType){
20017     
20018     this.meta = meta;
20019     
20020     this.recordType = recordType instanceof Array ? 
20021         Roo.data.Record.create(recordType) : recordType;
20022 };
20023
20024 Roo.data.DataReader.prototype = {
20025      /**
20026      * Create an empty record
20027      * @param {Object} data (optional) - overlay some values
20028      * @return {Roo.data.Record} record created.
20029      */
20030     newRow :  function(d) {
20031         var da =  {};
20032         this.recordType.prototype.fields.each(function(c) {
20033             switch( c.type) {
20034                 case 'int' : da[c.name] = 0; break;
20035                 case 'date' : da[c.name] = new Date(); break;
20036                 case 'float' : da[c.name] = 0.0; break;
20037                 case 'boolean' : da[c.name] = false; break;
20038                 default : da[c.name] = ""; break;
20039             }
20040             
20041         });
20042         return new this.recordType(Roo.apply(da, d));
20043     }
20044     
20045 };/*
20046  * Based on:
20047  * Ext JS Library 1.1.1
20048  * Copyright(c) 2006-2007, Ext JS, LLC.
20049  *
20050  * Originally Released Under LGPL - original licence link has changed is not relivant.
20051  *
20052  * Fork - LGPL
20053  * <script type="text/javascript">
20054  */
20055
20056 /**
20057  * @class Roo.data.DataProxy
20058  * @extends Roo.data.Observable
20059  * This class is an abstract base class for implementations which provide retrieval of
20060  * unformatted data objects.<br>
20061  * <p>
20062  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20063  * (of the appropriate type which knows how to parse the data object) to provide a block of
20064  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20065  * <p>
20066  * Custom implementations must implement the load method as described in
20067  * {@link Roo.data.HttpProxy#load}.
20068  */
20069 Roo.data.DataProxy = function(){
20070     this.addEvents({
20071         /**
20072          * @event beforeload
20073          * Fires before a network request is made to retrieve a data object.
20074          * @param {Object} This DataProxy object.
20075          * @param {Object} params The params parameter to the load function.
20076          */
20077         beforeload : true,
20078         /**
20079          * @event load
20080          * Fires before the load method's callback is called.
20081          * @param {Object} This DataProxy object.
20082          * @param {Object} o The data object.
20083          * @param {Object} arg The callback argument object passed to the load function.
20084          */
20085         load : true,
20086         /**
20087          * @event loadexception
20088          * Fires if an Exception occurs during data retrieval.
20089          * @param {Object} This DataProxy object.
20090          * @param {Object} o The data object.
20091          * @param {Object} arg The callback argument object passed to the load function.
20092          * @param {Object} e The Exception.
20093          */
20094         loadexception : true
20095     });
20096     Roo.data.DataProxy.superclass.constructor.call(this);
20097 };
20098
20099 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20100
20101     /**
20102      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20103      */
20104 /*
20105  * Based on:
20106  * Ext JS Library 1.1.1
20107  * Copyright(c) 2006-2007, Ext JS, LLC.
20108  *
20109  * Originally Released Under LGPL - original licence link has changed is not relivant.
20110  *
20111  * Fork - LGPL
20112  * <script type="text/javascript">
20113  */
20114 /**
20115  * @class Roo.data.MemoryProxy
20116  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20117  * to the Reader when its load method is called.
20118  * @constructor
20119  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20120  */
20121 Roo.data.MemoryProxy = function(data){
20122     if (data.data) {
20123         data = data.data;
20124     }
20125     Roo.data.MemoryProxy.superclass.constructor.call(this);
20126     this.data = data;
20127 };
20128
20129 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20130     /**
20131      * Load data from the requested source (in this case an in-memory
20132      * data object passed to the constructor), read the data object into
20133      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20134      * process that block using the passed callback.
20135      * @param {Object} params This parameter is not used by the MemoryProxy class.
20136      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20137      * object into a block of Roo.data.Records.
20138      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20139      * The function must be passed <ul>
20140      * <li>The Record block object</li>
20141      * <li>The "arg" argument from the load function</li>
20142      * <li>A boolean success indicator</li>
20143      * </ul>
20144      * @param {Object} scope The scope in which to call the callback
20145      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20146      */
20147     load : function(params, reader, callback, scope, arg){
20148         params = params || {};
20149         var result;
20150         try {
20151             result = reader.readRecords(this.data);
20152         }catch(e){
20153             this.fireEvent("loadexception", this, arg, null, e);
20154             callback.call(scope, null, arg, false);
20155             return;
20156         }
20157         callback.call(scope, result, arg, true);
20158     },
20159     
20160     // private
20161     update : function(params, records){
20162         
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174 /**
20175  * @class Roo.data.HttpProxy
20176  * @extends Roo.data.DataProxy
20177  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20178  * configured to reference a certain URL.<br><br>
20179  * <p>
20180  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20181  * from which the running page was served.<br><br>
20182  * <p>
20183  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20184  * <p>
20185  * Be aware that to enable the browser to parse an XML document, the server must set
20186  * the Content-Type header in the HTTP response to "text/xml".
20187  * @constructor
20188  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20189  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20190  * will be used to make the request.
20191  */
20192 Roo.data.HttpProxy = function(conn){
20193     Roo.data.HttpProxy.superclass.constructor.call(this);
20194     // is conn a conn config or a real conn?
20195     this.conn = conn;
20196     this.useAjax = !conn || !conn.events;
20197   
20198 };
20199
20200 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20201     // thse are take from connection...
20202     
20203     /**
20204      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20205      */
20206     /**
20207      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20208      * extra parameters to each request made by this object. (defaults to undefined)
20209      */
20210     /**
20211      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20212      *  to each request made by this object. (defaults to undefined)
20213      */
20214     /**
20215      * @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)
20216      */
20217     /**
20218      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20219      */
20220      /**
20221      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20222      * @type Boolean
20223      */
20224   
20225
20226     /**
20227      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20228      * @type Boolean
20229      */
20230     /**
20231      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20232      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20233      * a finer-grained basis than the DataProxy events.
20234      */
20235     getConnection : function(){
20236         return this.useAjax ? Roo.Ajax : this.conn;
20237     },
20238
20239     /**
20240      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20241      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20242      * process that block using the passed callback.
20243      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20244      * for the request to the remote server.
20245      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20246      * object into a block of Roo.data.Records.
20247      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20248      * The function must be passed <ul>
20249      * <li>The Record block object</li>
20250      * <li>The "arg" argument from the load function</li>
20251      * <li>A boolean success indicator</li>
20252      * </ul>
20253      * @param {Object} scope The scope in which to call the callback
20254      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20255      */
20256     load : function(params, reader, callback, scope, arg){
20257         if(this.fireEvent("beforeload", this, params) !== false){
20258             var  o = {
20259                 params : params || {},
20260                 request: {
20261                     callback : callback,
20262                     scope : scope,
20263                     arg : arg
20264                 },
20265                 reader: reader,
20266                 callback : this.loadResponse,
20267                 scope: this
20268             };
20269             if(this.useAjax){
20270                 Roo.applyIf(o, this.conn);
20271                 if(this.activeRequest){
20272                     Roo.Ajax.abort(this.activeRequest);
20273                 }
20274                 this.activeRequest = Roo.Ajax.request(o);
20275             }else{
20276                 this.conn.request(o);
20277             }
20278         }else{
20279             callback.call(scope||this, null, arg, false);
20280         }
20281     },
20282
20283     // private
20284     loadResponse : function(o, success, response){
20285         delete this.activeRequest;
20286         if(!success){
20287             this.fireEvent("loadexception", this, o, response);
20288             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20289             return;
20290         }
20291         var result;
20292         try {
20293             result = o.reader.read(response);
20294         }catch(e){
20295             this.fireEvent("loadexception", this, o, response, e);
20296             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20297             return;
20298         }
20299         
20300         this.fireEvent("load", this, o, o.request.arg);
20301         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20302     },
20303
20304     // private
20305     update : function(dataSet){
20306
20307     },
20308
20309     // private
20310     updateResponse : function(dataSet){
20311
20312     }
20313 });/*
20314  * Based on:
20315  * Ext JS Library 1.1.1
20316  * Copyright(c) 2006-2007, Ext JS, LLC.
20317  *
20318  * Originally Released Under LGPL - original licence link has changed is not relivant.
20319  *
20320  * Fork - LGPL
20321  * <script type="text/javascript">
20322  */
20323
20324 /**
20325  * @class Roo.data.ScriptTagProxy
20326  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20327  * other than the originating domain of the running page.<br><br>
20328  * <p>
20329  * <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
20330  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20331  * <p>
20332  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20333  * source code that is used as the source inside a &lt;script> tag.<br><br>
20334  * <p>
20335  * In order for the browser to process the returned data, the server must wrap the data object
20336  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20337  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20338  * depending on whether the callback name was passed:
20339  * <p>
20340  * <pre><code>
20341 boolean scriptTag = false;
20342 String cb = request.getParameter("callback");
20343 if (cb != null) {
20344     scriptTag = true;
20345     response.setContentType("text/javascript");
20346 } else {
20347     response.setContentType("application/x-json");
20348 }
20349 Writer out = response.getWriter();
20350 if (scriptTag) {
20351     out.write(cb + "(");
20352 }
20353 out.print(dataBlock.toJsonString());
20354 if (scriptTag) {
20355     out.write(");");
20356 }
20357 </pre></code>
20358  *
20359  * @constructor
20360  * @param {Object} config A configuration object.
20361  */
20362 Roo.data.ScriptTagProxy = function(config){
20363     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20364     Roo.apply(this, config);
20365     this.head = document.getElementsByTagName("head")[0];
20366 };
20367
20368 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20369
20370 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20371     /**
20372      * @cfg {String} url The URL from which to request the data object.
20373      */
20374     /**
20375      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20376      */
20377     timeout : 30000,
20378     /**
20379      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20380      * the server the name of the callback function set up by the load call to process the returned data object.
20381      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20382      * javascript output which calls this named function passing the data object as its only parameter.
20383      */
20384     callbackParam : "callback",
20385     /**
20386      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20387      * name to the request.
20388      */
20389     nocache : true,
20390
20391     /**
20392      * Load data from the configured URL, read the data object into
20393      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20394      * process that block using the passed callback.
20395      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20396      * for the request to the remote server.
20397      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20398      * object into a block of Roo.data.Records.
20399      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20400      * The function must be passed <ul>
20401      * <li>The Record block object</li>
20402      * <li>The "arg" argument from the load function</li>
20403      * <li>A boolean success indicator</li>
20404      * </ul>
20405      * @param {Object} scope The scope in which to call the callback
20406      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20407      */
20408     load : function(params, reader, callback, scope, arg){
20409         if(this.fireEvent("beforeload", this, params) !== false){
20410
20411             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20412
20413             var url = this.url;
20414             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20415             if(this.nocache){
20416                 url += "&_dc=" + (new Date().getTime());
20417             }
20418             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20419             var trans = {
20420                 id : transId,
20421                 cb : "stcCallback"+transId,
20422                 scriptId : "stcScript"+transId,
20423                 params : params,
20424                 arg : arg,
20425                 url : url,
20426                 callback : callback,
20427                 scope : scope,
20428                 reader : reader
20429             };
20430             var conn = this;
20431
20432             window[trans.cb] = function(o){
20433                 conn.handleResponse(o, trans);
20434             };
20435
20436             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20437
20438             if(this.autoAbort !== false){
20439                 this.abort();
20440             }
20441
20442             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20443
20444             var script = document.createElement("script");
20445             script.setAttribute("src", url);
20446             script.setAttribute("type", "text/javascript");
20447             script.setAttribute("id", trans.scriptId);
20448             this.head.appendChild(script);
20449
20450             this.trans = trans;
20451         }else{
20452             callback.call(scope||this, null, arg, false);
20453         }
20454     },
20455
20456     // private
20457     isLoading : function(){
20458         return this.trans ? true : false;
20459     },
20460
20461     /**
20462      * Abort the current server request.
20463      */
20464     abort : function(){
20465         if(this.isLoading()){
20466             this.destroyTrans(this.trans);
20467         }
20468     },
20469
20470     // private
20471     destroyTrans : function(trans, isLoaded){
20472         this.head.removeChild(document.getElementById(trans.scriptId));
20473         clearTimeout(trans.timeoutId);
20474         if(isLoaded){
20475             window[trans.cb] = undefined;
20476             try{
20477                 delete window[trans.cb];
20478             }catch(e){}
20479         }else{
20480             // if hasn't been loaded, wait for load to remove it to prevent script error
20481             window[trans.cb] = function(){
20482                 window[trans.cb] = undefined;
20483                 try{
20484                     delete window[trans.cb];
20485                 }catch(e){}
20486             };
20487         }
20488     },
20489
20490     // private
20491     handleResponse : function(o, trans){
20492         this.trans = false;
20493         this.destroyTrans(trans, true);
20494         var result;
20495         try {
20496             result = trans.reader.readRecords(o);
20497         }catch(e){
20498             this.fireEvent("loadexception", this, o, trans.arg, e);
20499             trans.callback.call(trans.scope||window, null, trans.arg, false);
20500             return;
20501         }
20502         this.fireEvent("load", this, o, trans.arg);
20503         trans.callback.call(trans.scope||window, result, trans.arg, true);
20504     },
20505
20506     // private
20507     handleFailure : function(trans){
20508         this.trans = false;
20509         this.destroyTrans(trans, false);
20510         this.fireEvent("loadexception", this, null, trans.arg);
20511         trans.callback.call(trans.scope||window, null, trans.arg, false);
20512     }
20513 });/*
20514  * Based on:
20515  * Ext JS Library 1.1.1
20516  * Copyright(c) 2006-2007, Ext JS, LLC.
20517  *
20518  * Originally Released Under LGPL - original licence link has changed is not relivant.
20519  *
20520  * Fork - LGPL
20521  * <script type="text/javascript">
20522  */
20523
20524 /**
20525  * @class Roo.data.JsonReader
20526  * @extends Roo.data.DataReader
20527  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20528  * based on mappings in a provided Roo.data.Record constructor.
20529  * 
20530  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20531  * in the reply previously. 
20532  * 
20533  * <p>
20534  * Example code:
20535  * <pre><code>
20536 var RecordDef = Roo.data.Record.create([
20537     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20538     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20539 ]);
20540 var myReader = new Roo.data.JsonReader({
20541     totalProperty: "results",    // The property which contains the total dataset size (optional)
20542     root: "rows",                // The property which contains an Array of row objects
20543     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20544 }, RecordDef);
20545 </code></pre>
20546  * <p>
20547  * This would consume a JSON file like this:
20548  * <pre><code>
20549 { 'results': 2, 'rows': [
20550     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20551     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20552 }
20553 </code></pre>
20554  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20555  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20556  * paged from the remote server.
20557  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20558  * @cfg {String} root name of the property which contains the Array of row objects.
20559  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20560  * @constructor
20561  * Create a new JsonReader
20562  * @param {Object} meta Metadata configuration options
20563  * @param {Object} recordType Either an Array of field definition objects,
20564  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20565  */
20566 Roo.data.JsonReader = function(meta, recordType){
20567     
20568     meta = meta || {};
20569     // set some defaults:
20570     Roo.applyIf(meta, {
20571         totalProperty: 'total',
20572         successProperty : 'success',
20573         root : 'data',
20574         id : 'id'
20575     });
20576     
20577     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20578 };
20579 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20580     
20581     /**
20582      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20583      * Used by Store query builder to append _requestMeta to params.
20584      * 
20585      */
20586     metaFromRemote : false,
20587     /**
20588      * This method is only used by a DataProxy which has retrieved data from a remote server.
20589      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20590      * @return {Object} data A data block which is used by an Roo.data.Store object as
20591      * a cache of Roo.data.Records.
20592      */
20593     read : function(response){
20594         var json = response.responseText;
20595        
20596         var o = /* eval:var:o */ eval("("+json+")");
20597         if(!o) {
20598             throw {message: "JsonReader.read: Json object not found"};
20599         }
20600         
20601         if(o.metaData){
20602             
20603             delete this.ef;
20604             this.metaFromRemote = true;
20605             this.meta = o.metaData;
20606             this.recordType = Roo.data.Record.create(o.metaData.fields);
20607             this.onMetaChange(this.meta, this.recordType, o);
20608         }
20609         return this.readRecords(o);
20610     },
20611
20612     // private function a store will implement
20613     onMetaChange : function(meta, recordType, o){
20614
20615     },
20616
20617     /**
20618          * @ignore
20619          */
20620     simpleAccess: function(obj, subsc) {
20621         return obj[subsc];
20622     },
20623
20624         /**
20625          * @ignore
20626          */
20627     getJsonAccessor: function(){
20628         var re = /[\[\.]/;
20629         return function(expr) {
20630             try {
20631                 return(re.test(expr))
20632                     ? new Function("obj", "return obj." + expr)
20633                     : function(obj){
20634                         return obj[expr];
20635                     };
20636             } catch(e){}
20637             return Roo.emptyFn;
20638         };
20639     }(),
20640
20641     /**
20642      * Create a data block containing Roo.data.Records from an XML document.
20643      * @param {Object} o An object which contains an Array of row objects in the property specified
20644      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20645      * which contains the total size of the dataset.
20646      * @return {Object} data A data block which is used by an Roo.data.Store object as
20647      * a cache of Roo.data.Records.
20648      */
20649     readRecords : function(o){
20650         /**
20651          * After any data loads, the raw JSON data is available for further custom processing.
20652          * @type Object
20653          */
20654         this.jsonData = o;
20655         var s = this.meta, Record = this.recordType,
20656             f = Record.prototype.fields, fi = f.items, fl = f.length;
20657
20658 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20659         if (!this.ef) {
20660             if(s.totalProperty) {
20661                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20662                 }
20663                 if(s.successProperty) {
20664                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20665                 }
20666                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20667                 if (s.id) {
20668                         var g = this.getJsonAccessor(s.id);
20669                         this.getId = function(rec) {
20670                                 var r = g(rec);
20671                                 return (r === undefined || r === "") ? null : r;
20672                         };
20673                 } else {
20674                         this.getId = function(){return null;};
20675                 }
20676             this.ef = [];
20677             for(var jj = 0; jj < fl; jj++){
20678                 f = fi[jj];
20679                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20680                 this.ef[jj] = this.getJsonAccessor(map);
20681             }
20682         }
20683
20684         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20685         if(s.totalProperty){
20686             var vt = parseInt(this.getTotal(o), 10);
20687             if(!isNaN(vt)){
20688                 totalRecords = vt;
20689             }
20690         }
20691         if(s.successProperty){
20692             var vs = this.getSuccess(o);
20693             if(vs === false || vs === 'false'){
20694                 success = false;
20695             }
20696         }
20697         var records = [];
20698             for(var i = 0; i < c; i++){
20699                     var n = root[i];
20700                 var values = {};
20701                 var id = this.getId(n);
20702                 for(var j = 0; j < fl; j++){
20703                     f = fi[j];
20704                 var v = this.ef[j](n);
20705                 if (!f.convert) {
20706                     Roo.log('missing convert for ' + f.name);
20707                     Roo.log(f);
20708                     continue;
20709                 }
20710                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20711                 }
20712                 var record = new Record(values, id);
20713                 record.json = n;
20714                 records[i] = record;
20715             }
20716             return {
20717                 success : success,
20718                 records : records,
20719                 totalRecords : totalRecords
20720             };
20721     }
20722 });/*
20723  * Based on:
20724  * Ext JS Library 1.1.1
20725  * Copyright(c) 2006-2007, Ext JS, LLC.
20726  *
20727  * Originally Released Under LGPL - original licence link has changed is not relivant.
20728  *
20729  * Fork - LGPL
20730  * <script type="text/javascript">
20731  */
20732
20733 /**
20734  * @class Roo.data.XmlReader
20735  * @extends Roo.data.DataReader
20736  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20737  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20738  * <p>
20739  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20740  * header in the HTTP response must be set to "text/xml".</em>
20741  * <p>
20742  * Example code:
20743  * <pre><code>
20744 var RecordDef = Roo.data.Record.create([
20745    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20746    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20747 ]);
20748 var myReader = new Roo.data.XmlReader({
20749    totalRecords: "results", // The element which contains the total dataset size (optional)
20750    record: "row",           // The repeated element which contains row information
20751    id: "id"                 // The element within the row that provides an ID for the record (optional)
20752 }, RecordDef);
20753 </code></pre>
20754  * <p>
20755  * This would consume an XML file like this:
20756  * <pre><code>
20757 &lt;?xml?>
20758 &lt;dataset>
20759  &lt;results>2&lt;/results>
20760  &lt;row>
20761    &lt;id>1&lt;/id>
20762    &lt;name>Bill&lt;/name>
20763    &lt;occupation>Gardener&lt;/occupation>
20764  &lt;/row>
20765  &lt;row>
20766    &lt;id>2&lt;/id>
20767    &lt;name>Ben&lt;/name>
20768    &lt;occupation>Horticulturalist&lt;/occupation>
20769  &lt;/row>
20770 &lt;/dataset>
20771 </code></pre>
20772  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20773  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20774  * paged from the remote server.
20775  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20776  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20777  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20778  * a record identifier value.
20779  * @constructor
20780  * Create a new XmlReader
20781  * @param {Object} meta Metadata configuration options
20782  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20783  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20784  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20785  */
20786 Roo.data.XmlReader = function(meta, recordType){
20787     meta = meta || {};
20788     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20789 };
20790 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20791     /**
20792      * This method is only used by a DataProxy which has retrieved data from a remote server.
20793          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20794          * to contain a method called 'responseXML' that returns an XML document object.
20795      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20796      * a cache of Roo.data.Records.
20797      */
20798     read : function(response){
20799         var doc = response.responseXML;
20800         if(!doc) {
20801             throw {message: "XmlReader.read: XML Document not available"};
20802         }
20803         return this.readRecords(doc);
20804     },
20805
20806     /**
20807      * Create a data block containing Roo.data.Records from an XML document.
20808          * @param {Object} doc A parsed XML document.
20809      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20810      * a cache of Roo.data.Records.
20811      */
20812     readRecords : function(doc){
20813         /**
20814          * After any data loads/reads, the raw XML Document is available for further custom processing.
20815          * @type XMLDocument
20816          */
20817         this.xmlData = doc;
20818         var root = doc.documentElement || doc;
20819         var q = Roo.DomQuery;
20820         var recordType = this.recordType, fields = recordType.prototype.fields;
20821         var sid = this.meta.id;
20822         var totalRecords = 0, success = true;
20823         if(this.meta.totalRecords){
20824             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20825         }
20826         
20827         if(this.meta.success){
20828             var sv = q.selectValue(this.meta.success, root, true);
20829             success = sv !== false && sv !== 'false';
20830         }
20831         var records = [];
20832         var ns = q.select(this.meta.record, root);
20833         for(var i = 0, len = ns.length; i < len; i++) {
20834                 var n = ns[i];
20835                 var values = {};
20836                 var id = sid ? q.selectValue(sid, n) : undefined;
20837                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20838                     var f = fields.items[j];
20839                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20840                     v = f.convert(v);
20841                     values[f.name] = v;
20842                 }
20843                 var record = new recordType(values, id);
20844                 record.node = n;
20845                 records[records.length] = record;
20846             }
20847
20848             return {
20849                 success : success,
20850                 records : records,
20851                 totalRecords : totalRecords || records.length
20852             };
20853     }
20854 });/*
20855  * Based on:
20856  * Ext JS Library 1.1.1
20857  * Copyright(c) 2006-2007, Ext JS, LLC.
20858  *
20859  * Originally Released Under LGPL - original licence link has changed is not relivant.
20860  *
20861  * Fork - LGPL
20862  * <script type="text/javascript">
20863  */
20864
20865 /**
20866  * @class Roo.data.ArrayReader
20867  * @extends Roo.data.DataReader
20868  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20869  * Each element of that Array represents a row of data fields. The
20870  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20871  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20872  * <p>
20873  * Example code:.
20874  * <pre><code>
20875 var RecordDef = Roo.data.Record.create([
20876     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20877     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20878 ]);
20879 var myReader = new Roo.data.ArrayReader({
20880     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20881 }, RecordDef);
20882 </code></pre>
20883  * <p>
20884  * This would consume an Array like this:
20885  * <pre><code>
20886 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20887   </code></pre>
20888  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20889  * @constructor
20890  * Create a new JsonReader
20891  * @param {Object} meta Metadata configuration options.
20892  * @param {Object} recordType Either an Array of field definition objects
20893  * as specified to {@link Roo.data.Record#create},
20894  * or an {@link Roo.data.Record} object
20895  * created using {@link Roo.data.Record#create}.
20896  */
20897 Roo.data.ArrayReader = function(meta, recordType){
20898     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20899 };
20900
20901 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20902     /**
20903      * Create a data block containing Roo.data.Records from an XML document.
20904      * @param {Object} o An Array of row objects which represents the dataset.
20905      * @return {Object} data A data block which is used by an Roo.data.Store object as
20906      * a cache of Roo.data.Records.
20907      */
20908     readRecords : function(o){
20909         var sid = this.meta ? this.meta.id : null;
20910         var recordType = this.recordType, fields = recordType.prototype.fields;
20911         var records = [];
20912         var root = o;
20913             for(var i = 0; i < root.length; i++){
20914                     var n = root[i];
20915                 var values = {};
20916                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20917                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20918                 var f = fields.items[j];
20919                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20920                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20921                 v = f.convert(v);
20922                 values[f.name] = v;
20923             }
20924                 var record = new recordType(values, id);
20925                 record.json = n;
20926                 records[records.length] = record;
20927             }
20928             return {
20929                 records : records,
20930                 totalRecords : records.length
20931             };
20932     }
20933 });/*
20934  * Based on:
20935  * Ext JS Library 1.1.1
20936  * Copyright(c) 2006-2007, Ext JS, LLC.
20937  *
20938  * Originally Released Under LGPL - original licence link has changed is not relivant.
20939  *
20940  * Fork - LGPL
20941  * <script type="text/javascript">
20942  */
20943
20944
20945 /**
20946  * @class Roo.data.Tree
20947  * @extends Roo.util.Observable
20948  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20949  * in the tree have most standard DOM functionality.
20950  * @constructor
20951  * @param {Node} root (optional) The root node
20952  */
20953 Roo.data.Tree = function(root){
20954    this.nodeHash = {};
20955    /**
20956     * The root node for this tree
20957     * @type Node
20958     */
20959    this.root = null;
20960    if(root){
20961        this.setRootNode(root);
20962    }
20963    this.addEvents({
20964        /**
20965         * @event append
20966         * Fires when a new child node is appended to a node in this tree.
20967         * @param {Tree} tree The owner tree
20968         * @param {Node} parent The parent node
20969         * @param {Node} node The newly appended node
20970         * @param {Number} index The index of the newly appended node
20971         */
20972        "append" : true,
20973        /**
20974         * @event remove
20975         * Fires when a child node is removed from a node in this tree.
20976         * @param {Tree} tree The owner tree
20977         * @param {Node} parent The parent node
20978         * @param {Node} node The child node removed
20979         */
20980        "remove" : true,
20981        /**
20982         * @event move
20983         * Fires when a node is moved to a new location in the tree
20984         * @param {Tree} tree The owner tree
20985         * @param {Node} node The node moved
20986         * @param {Node} oldParent The old parent of this node
20987         * @param {Node} newParent The new parent of this node
20988         * @param {Number} index The index it was moved to
20989         */
20990        "move" : true,
20991        /**
20992         * @event insert
20993         * Fires when a new child node is inserted in a node in this tree.
20994         * @param {Tree} tree The owner tree
20995         * @param {Node} parent The parent node
20996         * @param {Node} node The child node inserted
20997         * @param {Node} refNode The child node the node was inserted before
20998         */
20999        "insert" : true,
21000        /**
21001         * @event beforeappend
21002         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21003         * @param {Tree} tree The owner tree
21004         * @param {Node} parent The parent node
21005         * @param {Node} node The child node to be appended
21006         */
21007        "beforeappend" : true,
21008        /**
21009         * @event beforeremove
21010         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21011         * @param {Tree} tree The owner tree
21012         * @param {Node} parent The parent node
21013         * @param {Node} node The child node to be removed
21014         */
21015        "beforeremove" : true,
21016        /**
21017         * @event beforemove
21018         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21019         * @param {Tree} tree The owner tree
21020         * @param {Node} node The node being moved
21021         * @param {Node} oldParent The parent of the node
21022         * @param {Node} newParent The new parent the node is moving to
21023         * @param {Number} index The index it is being moved to
21024         */
21025        "beforemove" : true,
21026        /**
21027         * @event beforeinsert
21028         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21029         * @param {Tree} tree The owner tree
21030         * @param {Node} parent The parent node
21031         * @param {Node} node The child node to be inserted
21032         * @param {Node} refNode The child node the node is being inserted before
21033         */
21034        "beforeinsert" : true
21035    });
21036
21037     Roo.data.Tree.superclass.constructor.call(this);
21038 };
21039
21040 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21041     pathSeparator: "/",
21042
21043     proxyNodeEvent : function(){
21044         return this.fireEvent.apply(this, arguments);
21045     },
21046
21047     /**
21048      * Returns the root node for this tree.
21049      * @return {Node}
21050      */
21051     getRootNode : function(){
21052         return this.root;
21053     },
21054
21055     /**
21056      * Sets the root node for this tree.
21057      * @param {Node} node
21058      * @return {Node}
21059      */
21060     setRootNode : function(node){
21061         this.root = node;
21062         node.ownerTree = this;
21063         node.isRoot = true;
21064         this.registerNode(node);
21065         return node;
21066     },
21067
21068     /**
21069      * Gets a node in this tree by its id.
21070      * @param {String} id
21071      * @return {Node}
21072      */
21073     getNodeById : function(id){
21074         return this.nodeHash[id];
21075     },
21076
21077     registerNode : function(node){
21078         this.nodeHash[node.id] = node;
21079     },
21080
21081     unregisterNode : function(node){
21082         delete this.nodeHash[node.id];
21083     },
21084
21085     toString : function(){
21086         return "[Tree"+(this.id?" "+this.id:"")+"]";
21087     }
21088 });
21089
21090 /**
21091  * @class Roo.data.Node
21092  * @extends Roo.util.Observable
21093  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21094  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21095  * @constructor
21096  * @param {Object} attributes The attributes/config for the node
21097  */
21098 Roo.data.Node = function(attributes){
21099     /**
21100      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21101      * @type {Object}
21102      */
21103     this.attributes = attributes || {};
21104     this.leaf = this.attributes.leaf;
21105     /**
21106      * The node id. @type String
21107      */
21108     this.id = this.attributes.id;
21109     if(!this.id){
21110         this.id = Roo.id(null, "ynode-");
21111         this.attributes.id = this.id;
21112     }
21113     /**
21114      * All child nodes of this node. @type Array
21115      */
21116     this.childNodes = [];
21117     if(!this.childNodes.indexOf){ // indexOf is a must
21118         this.childNodes.indexOf = function(o){
21119             for(var i = 0, len = this.length; i < len; i++){
21120                 if(this[i] == o) {
21121                     return i;
21122                 }
21123             }
21124             return -1;
21125         };
21126     }
21127     /**
21128      * The parent node for this node. @type Node
21129      */
21130     this.parentNode = null;
21131     /**
21132      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21133      */
21134     this.firstChild = null;
21135     /**
21136      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21137      */
21138     this.lastChild = null;
21139     /**
21140      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21141      */
21142     this.previousSibling = null;
21143     /**
21144      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21145      */
21146     this.nextSibling = null;
21147
21148     this.addEvents({
21149        /**
21150         * @event append
21151         * Fires when a new child node is appended
21152         * @param {Tree} tree The owner tree
21153         * @param {Node} this This node
21154         * @param {Node} node The newly appended node
21155         * @param {Number} index The index of the newly appended node
21156         */
21157        "append" : true,
21158        /**
21159         * @event remove
21160         * Fires when a child node is removed
21161         * @param {Tree} tree The owner tree
21162         * @param {Node} this This node
21163         * @param {Node} node The removed node
21164         */
21165        "remove" : true,
21166        /**
21167         * @event move
21168         * Fires when this node is moved to a new location in the tree
21169         * @param {Tree} tree The owner tree
21170         * @param {Node} this This node
21171         * @param {Node} oldParent The old parent of this node
21172         * @param {Node} newParent The new parent of this node
21173         * @param {Number} index The index it was moved to
21174         */
21175        "move" : true,
21176        /**
21177         * @event insert
21178         * Fires when a new child node is inserted.
21179         * @param {Tree} tree The owner tree
21180         * @param {Node} this This node
21181         * @param {Node} node The child node inserted
21182         * @param {Node} refNode The child node the node was inserted before
21183         */
21184        "insert" : true,
21185        /**
21186         * @event beforeappend
21187         * Fires before a new child is appended, return false to cancel the append.
21188         * @param {Tree} tree The owner tree
21189         * @param {Node} this This node
21190         * @param {Node} node The child node to be appended
21191         */
21192        "beforeappend" : true,
21193        /**
21194         * @event beforeremove
21195         * Fires before a child is removed, return false to cancel the remove.
21196         * @param {Tree} tree The owner tree
21197         * @param {Node} this This node
21198         * @param {Node} node The child node to be removed
21199         */
21200        "beforeremove" : true,
21201        /**
21202         * @event beforemove
21203         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21204         * @param {Tree} tree The owner tree
21205         * @param {Node} this This node
21206         * @param {Node} oldParent The parent of this node
21207         * @param {Node} newParent The new parent this node is moving to
21208         * @param {Number} index The index it is being moved to
21209         */
21210        "beforemove" : true,
21211        /**
21212         * @event beforeinsert
21213         * Fires before a new child is inserted, return false to cancel the insert.
21214         * @param {Tree} tree The owner tree
21215         * @param {Node} this This node
21216         * @param {Node} node The child node to be inserted
21217         * @param {Node} refNode The child node the node is being inserted before
21218         */
21219        "beforeinsert" : true
21220    });
21221     this.listeners = this.attributes.listeners;
21222     Roo.data.Node.superclass.constructor.call(this);
21223 };
21224
21225 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21226     fireEvent : function(evtName){
21227         // first do standard event for this node
21228         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21229             return false;
21230         }
21231         // then bubble it up to the tree if the event wasn't cancelled
21232         var ot = this.getOwnerTree();
21233         if(ot){
21234             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21235                 return false;
21236             }
21237         }
21238         return true;
21239     },
21240
21241     /**
21242      * Returns true if this node is a leaf
21243      * @return {Boolean}
21244      */
21245     isLeaf : function(){
21246         return this.leaf === true;
21247     },
21248
21249     // private
21250     setFirstChild : function(node){
21251         this.firstChild = node;
21252     },
21253
21254     //private
21255     setLastChild : function(node){
21256         this.lastChild = node;
21257     },
21258
21259
21260     /**
21261      * Returns true if this node is the last child of its parent
21262      * @return {Boolean}
21263      */
21264     isLast : function(){
21265        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21266     },
21267
21268     /**
21269      * Returns true if this node is the first child of its parent
21270      * @return {Boolean}
21271      */
21272     isFirst : function(){
21273        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21274     },
21275
21276     hasChildNodes : function(){
21277         return !this.isLeaf() && this.childNodes.length > 0;
21278     },
21279
21280     /**
21281      * Insert node(s) as the last child node of this node.
21282      * @param {Node/Array} node The node or Array of nodes to append
21283      * @return {Node} The appended node if single append, or null if an array was passed
21284      */
21285     appendChild : function(node){
21286         var multi = false;
21287         if(node instanceof Array){
21288             multi = node;
21289         }else if(arguments.length > 1){
21290             multi = arguments;
21291         }
21292         // if passed an array or multiple args do them one by one
21293         if(multi){
21294             for(var i = 0, len = multi.length; i < len; i++) {
21295                 this.appendChild(multi[i]);
21296             }
21297         }else{
21298             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21299                 return false;
21300             }
21301             var index = this.childNodes.length;
21302             var oldParent = node.parentNode;
21303             // it's a move, make sure we move it cleanly
21304             if(oldParent){
21305                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21306                     return false;
21307                 }
21308                 oldParent.removeChild(node);
21309             }
21310             index = this.childNodes.length;
21311             if(index == 0){
21312                 this.setFirstChild(node);
21313             }
21314             this.childNodes.push(node);
21315             node.parentNode = this;
21316             var ps = this.childNodes[index-1];
21317             if(ps){
21318                 node.previousSibling = ps;
21319                 ps.nextSibling = node;
21320             }else{
21321                 node.previousSibling = null;
21322             }
21323             node.nextSibling = null;
21324             this.setLastChild(node);
21325             node.setOwnerTree(this.getOwnerTree());
21326             this.fireEvent("append", this.ownerTree, this, node, index);
21327             if(oldParent){
21328                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21329             }
21330             return node;
21331         }
21332     },
21333
21334     /**
21335      * Removes a child node from this node.
21336      * @param {Node} node The node to remove
21337      * @return {Node} The removed node
21338      */
21339     removeChild : function(node){
21340         var index = this.childNodes.indexOf(node);
21341         if(index == -1){
21342             return false;
21343         }
21344         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21345             return false;
21346         }
21347
21348         // remove it from childNodes collection
21349         this.childNodes.splice(index, 1);
21350
21351         // update siblings
21352         if(node.previousSibling){
21353             node.previousSibling.nextSibling = node.nextSibling;
21354         }
21355         if(node.nextSibling){
21356             node.nextSibling.previousSibling = node.previousSibling;
21357         }
21358
21359         // update child refs
21360         if(this.firstChild == node){
21361             this.setFirstChild(node.nextSibling);
21362         }
21363         if(this.lastChild == node){
21364             this.setLastChild(node.previousSibling);
21365         }
21366
21367         node.setOwnerTree(null);
21368         // clear any references from the node
21369         node.parentNode = null;
21370         node.previousSibling = null;
21371         node.nextSibling = null;
21372         this.fireEvent("remove", this.ownerTree, this, node);
21373         return node;
21374     },
21375
21376     /**
21377      * Inserts the first node before the second node in this nodes childNodes collection.
21378      * @param {Node} node The node to insert
21379      * @param {Node} refNode The node to insert before (if null the node is appended)
21380      * @return {Node} The inserted node
21381      */
21382     insertBefore : function(node, refNode){
21383         if(!refNode){ // like standard Dom, refNode can be null for append
21384             return this.appendChild(node);
21385         }
21386         // nothing to do
21387         if(node == refNode){
21388             return false;
21389         }
21390
21391         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21392             return false;
21393         }
21394         var index = this.childNodes.indexOf(refNode);
21395         var oldParent = node.parentNode;
21396         var refIndex = index;
21397
21398         // when moving internally, indexes will change after remove
21399         if(oldParent == this && this.childNodes.indexOf(node) < index){
21400             refIndex--;
21401         }
21402
21403         // it's a move, make sure we move it cleanly
21404         if(oldParent){
21405             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21406                 return false;
21407             }
21408             oldParent.removeChild(node);
21409         }
21410         if(refIndex == 0){
21411             this.setFirstChild(node);
21412         }
21413         this.childNodes.splice(refIndex, 0, node);
21414         node.parentNode = this;
21415         var ps = this.childNodes[refIndex-1];
21416         if(ps){
21417             node.previousSibling = ps;
21418             ps.nextSibling = node;
21419         }else{
21420             node.previousSibling = null;
21421         }
21422         node.nextSibling = refNode;
21423         refNode.previousSibling = node;
21424         node.setOwnerTree(this.getOwnerTree());
21425         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21426         if(oldParent){
21427             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21428         }
21429         return node;
21430     },
21431
21432     /**
21433      * Returns the child node at the specified index.
21434      * @param {Number} index
21435      * @return {Node}
21436      */
21437     item : function(index){
21438         return this.childNodes[index];
21439     },
21440
21441     /**
21442      * Replaces one child node in this node with another.
21443      * @param {Node} newChild The replacement node
21444      * @param {Node} oldChild The node to replace
21445      * @return {Node} The replaced node
21446      */
21447     replaceChild : function(newChild, oldChild){
21448         this.insertBefore(newChild, oldChild);
21449         this.removeChild(oldChild);
21450         return oldChild;
21451     },
21452
21453     /**
21454      * Returns the index of a child node
21455      * @param {Node} node
21456      * @return {Number} The index of the node or -1 if it was not found
21457      */
21458     indexOf : function(child){
21459         return this.childNodes.indexOf(child);
21460     },
21461
21462     /**
21463      * Returns the tree this node is in.
21464      * @return {Tree}
21465      */
21466     getOwnerTree : function(){
21467         // if it doesn't have one, look for one
21468         if(!this.ownerTree){
21469             var p = this;
21470             while(p){
21471                 if(p.ownerTree){
21472                     this.ownerTree = p.ownerTree;
21473                     break;
21474                 }
21475                 p = p.parentNode;
21476             }
21477         }
21478         return this.ownerTree;
21479     },
21480
21481     /**
21482      * Returns depth of this node (the root node has a depth of 0)
21483      * @return {Number}
21484      */
21485     getDepth : function(){
21486         var depth = 0;
21487         var p = this;
21488         while(p.parentNode){
21489             ++depth;
21490             p = p.parentNode;
21491         }
21492         return depth;
21493     },
21494
21495     // private
21496     setOwnerTree : function(tree){
21497         // if it's move, we need to update everyone
21498         if(tree != this.ownerTree){
21499             if(this.ownerTree){
21500                 this.ownerTree.unregisterNode(this);
21501             }
21502             this.ownerTree = tree;
21503             var cs = this.childNodes;
21504             for(var i = 0, len = cs.length; i < len; i++) {
21505                 cs[i].setOwnerTree(tree);
21506             }
21507             if(tree){
21508                 tree.registerNode(this);
21509             }
21510         }
21511     },
21512
21513     /**
21514      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21515      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21516      * @return {String} The path
21517      */
21518     getPath : function(attr){
21519         attr = attr || "id";
21520         var p = this.parentNode;
21521         var b = [this.attributes[attr]];
21522         while(p){
21523             b.unshift(p.attributes[attr]);
21524             p = p.parentNode;
21525         }
21526         var sep = this.getOwnerTree().pathSeparator;
21527         return sep + b.join(sep);
21528     },
21529
21530     /**
21531      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21532      * function call will be the scope provided or the current node. The arguments to the function
21533      * will be the args provided or the current node. If the function returns false at any point,
21534      * the bubble is stopped.
21535      * @param {Function} fn The function to call
21536      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21537      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21538      */
21539     bubble : function(fn, scope, args){
21540         var p = this;
21541         while(p){
21542             if(fn.call(scope || p, args || p) === false){
21543                 break;
21544             }
21545             p = p.parentNode;
21546         }
21547     },
21548
21549     /**
21550      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21551      * function call will be the scope provided or the current node. The arguments to the function
21552      * will be the args provided or the current node. If the function returns false at any point,
21553      * the cascade is stopped on that branch.
21554      * @param {Function} fn The function to call
21555      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21556      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21557      */
21558     cascade : function(fn, scope, args){
21559         if(fn.call(scope || this, args || this) !== false){
21560             var cs = this.childNodes;
21561             for(var i = 0, len = cs.length; i < len; i++) {
21562                 cs[i].cascade(fn, scope, args);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21569      * function call will be the scope provided or the current node. The arguments to the function
21570      * will be the args provided or the current node. If the function returns false at any point,
21571      * the iteration stops.
21572      * @param {Function} fn The function to call
21573      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21574      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21575      */
21576     eachChild : function(fn, scope, args){
21577         var cs = this.childNodes;
21578         for(var i = 0, len = cs.length; i < len; i++) {
21579                 if(fn.call(scope || this, args || cs[i]) === false){
21580                     break;
21581                 }
21582         }
21583     },
21584
21585     /**
21586      * Finds the first child that has the attribute with the specified value.
21587      * @param {String} attribute The attribute name
21588      * @param {Mixed} value The value to search for
21589      * @return {Node} The found child or null if none was found
21590      */
21591     findChild : function(attribute, value){
21592         var cs = this.childNodes;
21593         for(var i = 0, len = cs.length; i < len; i++) {
21594                 if(cs[i].attributes[attribute] == value){
21595                     return cs[i];
21596                 }
21597         }
21598         return null;
21599     },
21600
21601     /**
21602      * Finds the first child by a custom function. The child matches if the function passed
21603      * returns true.
21604      * @param {Function} fn
21605      * @param {Object} scope (optional)
21606      * @return {Node} The found child or null if none was found
21607      */
21608     findChildBy : function(fn, scope){
21609         var cs = this.childNodes;
21610         for(var i = 0, len = cs.length; i < len; i++) {
21611                 if(fn.call(scope||cs[i], cs[i]) === true){
21612                     return cs[i];
21613                 }
21614         }
21615         return null;
21616     },
21617
21618     /**
21619      * Sorts this nodes children using the supplied sort function
21620      * @param {Function} fn
21621      * @param {Object} scope (optional)
21622      */
21623     sort : function(fn, scope){
21624         var cs = this.childNodes;
21625         var len = cs.length;
21626         if(len > 0){
21627             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21628             cs.sort(sortFn);
21629             for(var i = 0; i < len; i++){
21630                 var n = cs[i];
21631                 n.previousSibling = cs[i-1];
21632                 n.nextSibling = cs[i+1];
21633                 if(i == 0){
21634                     this.setFirstChild(n);
21635                 }
21636                 if(i == len-1){
21637                     this.setLastChild(n);
21638                 }
21639             }
21640         }
21641     },
21642
21643     /**
21644      * Returns true if this node is an ancestor (at any point) of the passed node.
21645      * @param {Node} node
21646      * @return {Boolean}
21647      */
21648     contains : function(node){
21649         return node.isAncestor(this);
21650     },
21651
21652     /**
21653      * Returns true if the passed node is an ancestor (at any point) of this node.
21654      * @param {Node} node
21655      * @return {Boolean}
21656      */
21657     isAncestor : function(node){
21658         var p = this.parentNode;
21659         while(p){
21660             if(p == node){
21661                 return true;
21662             }
21663             p = p.parentNode;
21664         }
21665         return false;
21666     },
21667
21668     toString : function(){
21669         return "[Node"+(this.id?" "+this.id:"")+"]";
21670     }
21671 });/*
21672  * Based on:
21673  * Ext JS Library 1.1.1
21674  * Copyright(c) 2006-2007, Ext JS, LLC.
21675  *
21676  * Originally Released Under LGPL - original licence link has changed is not relivant.
21677  *
21678  * Fork - LGPL
21679  * <script type="text/javascript">
21680  */
21681  
21682
21683 /**
21684  * @class Roo.ComponentMgr
21685  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21686  * @singleton
21687  */
21688 Roo.ComponentMgr = function(){
21689     var all = new Roo.util.MixedCollection();
21690
21691     return {
21692         /**
21693          * Registers a component.
21694          * @param {Roo.Component} c The component
21695          */
21696         register : function(c){
21697             all.add(c);
21698         },
21699
21700         /**
21701          * Unregisters a component.
21702          * @param {Roo.Component} c The component
21703          */
21704         unregister : function(c){
21705             all.remove(c);
21706         },
21707
21708         /**
21709          * Returns a component by id
21710          * @param {String} id The component id
21711          */
21712         get : function(id){
21713             return all.get(id);
21714         },
21715
21716         /**
21717          * Registers a function that will be called when a specified component is added to ComponentMgr
21718          * @param {String} id The component id
21719          * @param {Funtction} fn The callback function
21720          * @param {Object} scope The scope of the callback
21721          */
21722         onAvailable : function(id, fn, scope){
21723             all.on("add", function(index, o){
21724                 if(o.id == id){
21725                     fn.call(scope || o, o);
21726                     all.un("add", fn, scope);
21727                 }
21728             });
21729         }
21730     };
21731 }();/*
21732  * Based on:
21733  * Ext JS Library 1.1.1
21734  * Copyright(c) 2006-2007, Ext JS, LLC.
21735  *
21736  * Originally Released Under LGPL - original licence link has changed is not relivant.
21737  *
21738  * Fork - LGPL
21739  * <script type="text/javascript">
21740  */
21741  
21742 /**
21743  * @class Roo.Component
21744  * @extends Roo.util.Observable
21745  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21746  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21747  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21748  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21749  * All visual components (widgets) that require rendering into a layout should subclass Component.
21750  * @constructor
21751  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21752  * 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
21753  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21754  */
21755 Roo.Component = function(config){
21756     config = config || {};
21757     if(config.tagName || config.dom || typeof config == "string"){ // element object
21758         config = {el: config, id: config.id || config};
21759     }
21760     this.initialConfig = config;
21761
21762     Roo.apply(this, config);
21763     this.addEvents({
21764         /**
21765          * @event disable
21766          * Fires after the component is disabled.
21767              * @param {Roo.Component} this
21768              */
21769         disable : true,
21770         /**
21771          * @event enable
21772          * Fires after the component is enabled.
21773              * @param {Roo.Component} this
21774              */
21775         enable : true,
21776         /**
21777          * @event beforeshow
21778          * Fires before the component is shown.  Return false to stop the show.
21779              * @param {Roo.Component} this
21780              */
21781         beforeshow : true,
21782         /**
21783          * @event show
21784          * Fires after the component is shown.
21785              * @param {Roo.Component} this
21786              */
21787         show : true,
21788         /**
21789          * @event beforehide
21790          * Fires before the component is hidden. Return false to stop the hide.
21791              * @param {Roo.Component} this
21792              */
21793         beforehide : true,
21794         /**
21795          * @event hide
21796          * Fires after the component is hidden.
21797              * @param {Roo.Component} this
21798              */
21799         hide : true,
21800         /**
21801          * @event beforerender
21802          * Fires before the component is rendered. Return false to stop the render.
21803              * @param {Roo.Component} this
21804              */
21805         beforerender : true,
21806         /**
21807          * @event render
21808          * Fires after the component is rendered.
21809              * @param {Roo.Component} this
21810              */
21811         render : true,
21812         /**
21813          * @event beforedestroy
21814          * Fires before the component is destroyed. Return false to stop the destroy.
21815              * @param {Roo.Component} this
21816              */
21817         beforedestroy : true,
21818         /**
21819          * @event destroy
21820          * Fires after the component is destroyed.
21821              * @param {Roo.Component} this
21822              */
21823         destroy : true
21824     });
21825     if(!this.id){
21826         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21827     }
21828     Roo.ComponentMgr.register(this);
21829     Roo.Component.superclass.constructor.call(this);
21830     this.initComponent();
21831     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21832         this.render(this.renderTo);
21833         delete this.renderTo;
21834     }
21835 };
21836
21837 // private
21838 Roo.Component.AUTO_ID = 1000;
21839
21840 Roo.extend(Roo.Component, Roo.util.Observable, {
21841     /**
21842      * @property {Boolean} hidden
21843      * true if this component is hidden. Read-only.
21844      */
21845     hidden : false,
21846     /**
21847      * true if this component is disabled. Read-only.
21848      */
21849     disabled : false,
21850     /**
21851      * true if this component has been rendered. Read-only.
21852      */
21853     rendered : false,
21854     
21855     /** @cfg {String} disableClass
21856      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21857      */
21858     disabledClass : "x-item-disabled",
21859         /** @cfg {Boolean} allowDomMove
21860          * Whether the component can move the Dom node when rendering (defaults to true).
21861          */
21862     allowDomMove : true,
21863     /** @cfg {String} hideMode
21864      * How this component should hidden. Supported values are
21865      * "visibility" (css visibility), "offsets" (negative offset position) and
21866      * "display" (css display) - defaults to "display".
21867      */
21868     hideMode: 'display',
21869
21870     // private
21871     ctype : "Roo.Component",
21872
21873     /** @cfg {String} actionMode 
21874      * which property holds the element that used for  hide() / show() / disable() / enable()
21875      * default is 'el' 
21876      */
21877     actionMode : "el",
21878
21879     // private
21880     getActionEl : function(){
21881         return this[this.actionMode];
21882     },
21883
21884     initComponent : Roo.emptyFn,
21885     /**
21886      * If this is a lazy rendering component, render it to its container element.
21887      * @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.
21888      */
21889     render : function(container, position){
21890         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21891             if(!container && this.el){
21892                 this.el = Roo.get(this.el);
21893                 container = this.el.dom.parentNode;
21894                 this.allowDomMove = false;
21895             }
21896             this.container = Roo.get(container);
21897             this.rendered = true;
21898             if(position !== undefined){
21899                 if(typeof position == 'number'){
21900                     position = this.container.dom.childNodes[position];
21901                 }else{
21902                     position = Roo.getDom(position);
21903                 }
21904             }
21905             this.onRender(this.container, position || null);
21906             if(this.cls){
21907                 this.el.addClass(this.cls);
21908                 delete this.cls;
21909             }
21910             if(this.style){
21911                 this.el.applyStyles(this.style);
21912                 delete this.style;
21913             }
21914             this.fireEvent("render", this);
21915             this.afterRender(this.container);
21916             if(this.hidden){
21917                 this.hide();
21918             }
21919             if(this.disabled){
21920                 this.disable();
21921             }
21922         }
21923         return this;
21924     },
21925
21926     // private
21927     // default function is not really useful
21928     onRender : function(ct, position){
21929         if(this.el){
21930             this.el = Roo.get(this.el);
21931             if(this.allowDomMove !== false){
21932                 ct.dom.insertBefore(this.el.dom, position);
21933             }
21934         }
21935     },
21936
21937     // private
21938     getAutoCreate : function(){
21939         var cfg = typeof this.autoCreate == "object" ?
21940                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21941         if(this.id && !cfg.id){
21942             cfg.id = this.id;
21943         }
21944         return cfg;
21945     },
21946
21947     // private
21948     afterRender : Roo.emptyFn,
21949
21950     /**
21951      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21952      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21953      */
21954     destroy : function(){
21955         if(this.fireEvent("beforedestroy", this) !== false){
21956             this.purgeListeners();
21957             this.beforeDestroy();
21958             if(this.rendered){
21959                 this.el.removeAllListeners();
21960                 this.el.remove();
21961                 if(this.actionMode == "container"){
21962                     this.container.remove();
21963                 }
21964             }
21965             this.onDestroy();
21966             Roo.ComponentMgr.unregister(this);
21967             this.fireEvent("destroy", this);
21968         }
21969     },
21970
21971         // private
21972     beforeDestroy : function(){
21973
21974     },
21975
21976         // private
21977         onDestroy : function(){
21978
21979     },
21980
21981     /**
21982      * Returns the underlying {@link Roo.Element}.
21983      * @return {Roo.Element} The element
21984      */
21985     getEl : function(){
21986         return this.el;
21987     },
21988
21989     /**
21990      * Returns the id of this component.
21991      * @return {String}
21992      */
21993     getId : function(){
21994         return this.id;
21995     },
21996
21997     /**
21998      * Try to focus this component.
21999      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22000      * @return {Roo.Component} this
22001      */
22002     focus : function(selectText){
22003         if(this.rendered){
22004             this.el.focus();
22005             if(selectText === true){
22006                 this.el.dom.select();
22007             }
22008         }
22009         return this;
22010     },
22011
22012     // private
22013     blur : function(){
22014         if(this.rendered){
22015             this.el.blur();
22016         }
22017         return this;
22018     },
22019
22020     /**
22021      * Disable this component.
22022      * @return {Roo.Component} this
22023      */
22024     disable : function(){
22025         if(this.rendered){
22026             this.onDisable();
22027         }
22028         this.disabled = true;
22029         this.fireEvent("disable", this);
22030         return this;
22031     },
22032
22033         // private
22034     onDisable : function(){
22035         this.getActionEl().addClass(this.disabledClass);
22036         this.el.dom.disabled = true;
22037     },
22038
22039     /**
22040      * Enable this component.
22041      * @return {Roo.Component} this
22042      */
22043     enable : function(){
22044         if(this.rendered){
22045             this.onEnable();
22046         }
22047         this.disabled = false;
22048         this.fireEvent("enable", this);
22049         return this;
22050     },
22051
22052         // private
22053     onEnable : function(){
22054         this.getActionEl().removeClass(this.disabledClass);
22055         this.el.dom.disabled = false;
22056     },
22057
22058     /**
22059      * Convenience function for setting disabled/enabled by boolean.
22060      * @param {Boolean} disabled
22061      */
22062     setDisabled : function(disabled){
22063         this[disabled ? "disable" : "enable"]();
22064     },
22065
22066     /**
22067      * Show this component.
22068      * @return {Roo.Component} this
22069      */
22070     show: function(){
22071         if(this.fireEvent("beforeshow", this) !== false){
22072             this.hidden = false;
22073             if(this.rendered){
22074                 this.onShow();
22075             }
22076             this.fireEvent("show", this);
22077         }
22078         return this;
22079     },
22080
22081     // private
22082     onShow : function(){
22083         var ae = this.getActionEl();
22084         if(this.hideMode == 'visibility'){
22085             ae.dom.style.visibility = "visible";
22086         }else if(this.hideMode == 'offsets'){
22087             ae.removeClass('x-hidden');
22088         }else{
22089             ae.dom.style.display = "";
22090         }
22091     },
22092
22093     /**
22094      * Hide this component.
22095      * @return {Roo.Component} this
22096      */
22097     hide: function(){
22098         if(this.fireEvent("beforehide", this) !== false){
22099             this.hidden = true;
22100             if(this.rendered){
22101                 this.onHide();
22102             }
22103             this.fireEvent("hide", this);
22104         }
22105         return this;
22106     },
22107
22108     // private
22109     onHide : function(){
22110         var ae = this.getActionEl();
22111         if(this.hideMode == 'visibility'){
22112             ae.dom.style.visibility = "hidden";
22113         }else if(this.hideMode == 'offsets'){
22114             ae.addClass('x-hidden');
22115         }else{
22116             ae.dom.style.display = "none";
22117         }
22118     },
22119
22120     /**
22121      * Convenience function to hide or show this component by boolean.
22122      * @param {Boolean} visible True to show, false to hide
22123      * @return {Roo.Component} this
22124      */
22125     setVisible: function(visible){
22126         if(visible) {
22127             this.show();
22128         }else{
22129             this.hide();
22130         }
22131         return this;
22132     },
22133
22134     /**
22135      * Returns true if this component is visible.
22136      */
22137     isVisible : function(){
22138         return this.getActionEl().isVisible();
22139     },
22140
22141     cloneConfig : function(overrides){
22142         overrides = overrides || {};
22143         var id = overrides.id || Roo.id();
22144         var cfg = Roo.applyIf(overrides, this.initialConfig);
22145         cfg.id = id; // prevent dup id
22146         return new this.constructor(cfg);
22147     }
22148 });/*
22149  * Based on:
22150  * Ext JS Library 1.1.1
22151  * Copyright(c) 2006-2007, Ext JS, LLC.
22152  *
22153  * Originally Released Under LGPL - original licence link has changed is not relivant.
22154  *
22155  * Fork - LGPL
22156  * <script type="text/javascript">
22157  */
22158  (function(){ 
22159 /**
22160  * @class Roo.Layer
22161  * @extends Roo.Element
22162  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22163  * automatic maintaining of shadow/shim positions.
22164  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22165  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22166  * you can pass a string with a CSS class name. False turns off the shadow.
22167  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22168  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22169  * @cfg {String} cls CSS class to add to the element
22170  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22171  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22172  * @constructor
22173  * @param {Object} config An object with config options.
22174  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22175  */
22176
22177 Roo.Layer = function(config, existingEl){
22178     config = config || {};
22179     var dh = Roo.DomHelper;
22180     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22181     if(existingEl){
22182         this.dom = Roo.getDom(existingEl);
22183     }
22184     if(!this.dom){
22185         var o = config.dh || {tag: "div", cls: "x-layer"};
22186         this.dom = dh.append(pel, o);
22187     }
22188     if(config.cls){
22189         this.addClass(config.cls);
22190     }
22191     this.constrain = config.constrain !== false;
22192     this.visibilityMode = Roo.Element.VISIBILITY;
22193     if(config.id){
22194         this.id = this.dom.id = config.id;
22195     }else{
22196         this.id = Roo.id(this.dom);
22197     }
22198     this.zindex = config.zindex || this.getZIndex();
22199     this.position("absolute", this.zindex);
22200     if(config.shadow){
22201         this.shadowOffset = config.shadowOffset || 4;
22202         this.shadow = new Roo.Shadow({
22203             offset : this.shadowOffset,
22204             mode : config.shadow
22205         });
22206     }else{
22207         this.shadowOffset = 0;
22208     }
22209     this.useShim = config.shim !== false && Roo.useShims;
22210     this.useDisplay = config.useDisplay;
22211     this.hide();
22212 };
22213
22214 var supr = Roo.Element.prototype;
22215
22216 // shims are shared among layer to keep from having 100 iframes
22217 var shims = [];
22218
22219 Roo.extend(Roo.Layer, Roo.Element, {
22220
22221     getZIndex : function(){
22222         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22223     },
22224
22225     getShim : function(){
22226         if(!this.useShim){
22227             return null;
22228         }
22229         if(this.shim){
22230             return this.shim;
22231         }
22232         var shim = shims.shift();
22233         if(!shim){
22234             shim = this.createShim();
22235             shim.enableDisplayMode('block');
22236             shim.dom.style.display = 'none';
22237             shim.dom.style.visibility = 'visible';
22238         }
22239         var pn = this.dom.parentNode;
22240         if(shim.dom.parentNode != pn){
22241             pn.insertBefore(shim.dom, this.dom);
22242         }
22243         shim.setStyle('z-index', this.getZIndex()-2);
22244         this.shim = shim;
22245         return shim;
22246     },
22247
22248     hideShim : function(){
22249         if(this.shim){
22250             this.shim.setDisplayed(false);
22251             shims.push(this.shim);
22252             delete this.shim;
22253         }
22254     },
22255
22256     disableShadow : function(){
22257         if(this.shadow){
22258             this.shadowDisabled = true;
22259             this.shadow.hide();
22260             this.lastShadowOffset = this.shadowOffset;
22261             this.shadowOffset = 0;
22262         }
22263     },
22264
22265     enableShadow : function(show){
22266         if(this.shadow){
22267             this.shadowDisabled = false;
22268             this.shadowOffset = this.lastShadowOffset;
22269             delete this.lastShadowOffset;
22270             if(show){
22271                 this.sync(true);
22272             }
22273         }
22274     },
22275
22276     // private
22277     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22278     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22279     sync : function(doShow){
22280         var sw = this.shadow;
22281         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22282             var sh = this.getShim();
22283
22284             var w = this.getWidth(),
22285                 h = this.getHeight();
22286
22287             var l = this.getLeft(true),
22288                 t = this.getTop(true);
22289
22290             if(sw && !this.shadowDisabled){
22291                 if(doShow && !sw.isVisible()){
22292                     sw.show(this);
22293                 }else{
22294                     sw.realign(l, t, w, h);
22295                 }
22296                 if(sh){
22297                     if(doShow){
22298                        sh.show();
22299                     }
22300                     // fit the shim behind the shadow, so it is shimmed too
22301                     var a = sw.adjusts, s = sh.dom.style;
22302                     s.left = (Math.min(l, l+a.l))+"px";
22303                     s.top = (Math.min(t, t+a.t))+"px";
22304                     s.width = (w+a.w)+"px";
22305                     s.height = (h+a.h)+"px";
22306                 }
22307             }else if(sh){
22308                 if(doShow){
22309                    sh.show();
22310                 }
22311                 sh.setSize(w, h);
22312                 sh.setLeftTop(l, t);
22313             }
22314             
22315         }
22316     },
22317
22318     // private
22319     destroy : function(){
22320         this.hideShim();
22321         if(this.shadow){
22322             this.shadow.hide();
22323         }
22324         this.removeAllListeners();
22325         var pn = this.dom.parentNode;
22326         if(pn){
22327             pn.removeChild(this.dom);
22328         }
22329         Roo.Element.uncache(this.id);
22330     },
22331
22332     remove : function(){
22333         this.destroy();
22334     },
22335
22336     // private
22337     beginUpdate : function(){
22338         this.updating = true;
22339     },
22340
22341     // private
22342     endUpdate : function(){
22343         this.updating = false;
22344         this.sync(true);
22345     },
22346
22347     // private
22348     hideUnders : function(negOffset){
22349         if(this.shadow){
22350             this.shadow.hide();
22351         }
22352         this.hideShim();
22353     },
22354
22355     // private
22356     constrainXY : function(){
22357         if(this.constrain){
22358             var vw = Roo.lib.Dom.getViewWidth(),
22359                 vh = Roo.lib.Dom.getViewHeight();
22360             var s = Roo.get(document).getScroll();
22361
22362             var xy = this.getXY();
22363             var x = xy[0], y = xy[1];   
22364             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22365             // only move it if it needs it
22366             var moved = false;
22367             // first validate right/bottom
22368             if((x + w) > vw+s.left){
22369                 x = vw - w - this.shadowOffset;
22370                 moved = true;
22371             }
22372             if((y + h) > vh+s.top){
22373                 y = vh - h - this.shadowOffset;
22374                 moved = true;
22375             }
22376             // then make sure top/left isn't negative
22377             if(x < s.left){
22378                 x = s.left;
22379                 moved = true;
22380             }
22381             if(y < s.top){
22382                 y = s.top;
22383                 moved = true;
22384             }
22385             if(moved){
22386                 if(this.avoidY){
22387                     var ay = this.avoidY;
22388                     if(y <= ay && (y+h) >= ay){
22389                         y = ay-h-5;   
22390                     }
22391                 }
22392                 xy = [x, y];
22393                 this.storeXY(xy);
22394                 supr.setXY.call(this, xy);
22395                 this.sync();
22396             }
22397         }
22398     },
22399
22400     isVisible : function(){
22401         return this.visible;    
22402     },
22403
22404     // private
22405     showAction : function(){
22406         this.visible = true; // track visibility to prevent getStyle calls
22407         if(this.useDisplay === true){
22408             this.setDisplayed("");
22409         }else if(this.lastXY){
22410             supr.setXY.call(this, this.lastXY);
22411         }else if(this.lastLT){
22412             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22413         }
22414     },
22415
22416     // private
22417     hideAction : function(){
22418         this.visible = false;
22419         if(this.useDisplay === true){
22420             this.setDisplayed(false);
22421         }else{
22422             this.setLeftTop(-10000,-10000);
22423         }
22424     },
22425
22426     // overridden Element method
22427     setVisible : function(v, a, d, c, e){
22428         if(v){
22429             this.showAction();
22430         }
22431         if(a && v){
22432             var cb = function(){
22433                 this.sync(true);
22434                 if(c){
22435                     c();
22436                 }
22437             }.createDelegate(this);
22438             supr.setVisible.call(this, true, true, d, cb, e);
22439         }else{
22440             if(!v){
22441                 this.hideUnders(true);
22442             }
22443             var cb = c;
22444             if(a){
22445                 cb = function(){
22446                     this.hideAction();
22447                     if(c){
22448                         c();
22449                     }
22450                 }.createDelegate(this);
22451             }
22452             supr.setVisible.call(this, v, a, d, cb, e);
22453             if(v){
22454                 this.sync(true);
22455             }else if(!a){
22456                 this.hideAction();
22457             }
22458         }
22459     },
22460
22461     storeXY : function(xy){
22462         delete this.lastLT;
22463         this.lastXY = xy;
22464     },
22465
22466     storeLeftTop : function(left, top){
22467         delete this.lastXY;
22468         this.lastLT = [left, top];
22469     },
22470
22471     // private
22472     beforeFx : function(){
22473         this.beforeAction();
22474         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22475     },
22476
22477     // private
22478     afterFx : function(){
22479         Roo.Layer.superclass.afterFx.apply(this, arguments);
22480         this.sync(this.isVisible());
22481     },
22482
22483     // private
22484     beforeAction : function(){
22485         if(!this.updating && this.shadow){
22486             this.shadow.hide();
22487         }
22488     },
22489
22490     // overridden Element method
22491     setLeft : function(left){
22492         this.storeLeftTop(left, this.getTop(true));
22493         supr.setLeft.apply(this, arguments);
22494         this.sync();
22495     },
22496
22497     setTop : function(top){
22498         this.storeLeftTop(this.getLeft(true), top);
22499         supr.setTop.apply(this, arguments);
22500         this.sync();
22501     },
22502
22503     setLeftTop : function(left, top){
22504         this.storeLeftTop(left, top);
22505         supr.setLeftTop.apply(this, arguments);
22506         this.sync();
22507     },
22508
22509     setXY : function(xy, a, d, c, e){
22510         this.fixDisplay();
22511         this.beforeAction();
22512         this.storeXY(xy);
22513         var cb = this.createCB(c);
22514         supr.setXY.call(this, xy, a, d, cb, e);
22515         if(!a){
22516             cb();
22517         }
22518     },
22519
22520     // private
22521     createCB : function(c){
22522         var el = this;
22523         return function(){
22524             el.constrainXY();
22525             el.sync(true);
22526             if(c){
22527                 c();
22528             }
22529         };
22530     },
22531
22532     // overridden Element method
22533     setX : function(x, a, d, c, e){
22534         this.setXY([x, this.getY()], a, d, c, e);
22535     },
22536
22537     // overridden Element method
22538     setY : function(y, a, d, c, e){
22539         this.setXY([this.getX(), y], a, d, c, e);
22540     },
22541
22542     // overridden Element method
22543     setSize : function(w, h, a, d, c, e){
22544         this.beforeAction();
22545         var cb = this.createCB(c);
22546         supr.setSize.call(this, w, h, a, d, cb, e);
22547         if(!a){
22548             cb();
22549         }
22550     },
22551
22552     // overridden Element method
22553     setWidth : function(w, a, d, c, e){
22554         this.beforeAction();
22555         var cb = this.createCB(c);
22556         supr.setWidth.call(this, w, a, d, cb, e);
22557         if(!a){
22558             cb();
22559         }
22560     },
22561
22562     // overridden Element method
22563     setHeight : function(h, a, d, c, e){
22564         this.beforeAction();
22565         var cb = this.createCB(c);
22566         supr.setHeight.call(this, h, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // overridden Element method
22573     setBounds : function(x, y, w, h, a, d, c, e){
22574         this.beforeAction();
22575         var cb = this.createCB(c);
22576         if(!a){
22577             this.storeXY([x, y]);
22578             supr.setXY.call(this, [x, y]);
22579             supr.setSize.call(this, w, h, a, d, cb, e);
22580             cb();
22581         }else{
22582             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22583         }
22584         return this;
22585     },
22586     
22587     /**
22588      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22589      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22590      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22591      * @param {Number} zindex The new z-index to set
22592      * @return {this} The Layer
22593      */
22594     setZIndex : function(zindex){
22595         this.zindex = zindex;
22596         this.setStyle("z-index", zindex + 2);
22597         if(this.shadow){
22598             this.shadow.setZIndex(zindex + 1);
22599         }
22600         if(this.shim){
22601             this.shim.setStyle("z-index", zindex);
22602         }
22603     }
22604 });
22605 })();/*
22606  * Based on:
22607  * Ext JS Library 1.1.1
22608  * Copyright(c) 2006-2007, Ext JS, LLC.
22609  *
22610  * Originally Released Under LGPL - original licence link has changed is not relivant.
22611  *
22612  * Fork - LGPL
22613  * <script type="text/javascript">
22614  */
22615
22616
22617 /**
22618  * @class Roo.Shadow
22619  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22620  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22621  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22622  * @constructor
22623  * Create a new Shadow
22624  * @param {Object} config The config object
22625  */
22626 Roo.Shadow = function(config){
22627     Roo.apply(this, config);
22628     if(typeof this.mode != "string"){
22629         this.mode = this.defaultMode;
22630     }
22631     var o = this.offset, a = {h: 0};
22632     var rad = Math.floor(this.offset/2);
22633     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22634         case "drop":
22635             a.w = 0;
22636             a.l = a.t = o;
22637             a.t -= 1;
22638             if(Roo.isIE){
22639                 a.l -= this.offset + rad;
22640                 a.t -= this.offset + rad;
22641                 a.w -= rad;
22642                 a.h -= rad;
22643                 a.t += 1;
22644             }
22645         break;
22646         case "sides":
22647             a.w = (o*2);
22648             a.l = -o;
22649             a.t = o-1;
22650             if(Roo.isIE){
22651                 a.l -= (this.offset - rad);
22652                 a.t -= this.offset + rad;
22653                 a.l += 1;
22654                 a.w -= (this.offset - rad)*2;
22655                 a.w -= rad + 1;
22656                 a.h -= 1;
22657             }
22658         break;
22659         case "frame":
22660             a.w = a.h = (o*2);
22661             a.l = a.t = -o;
22662             a.t += 1;
22663             a.h -= 2;
22664             if(Roo.isIE){
22665                 a.l -= (this.offset - rad);
22666                 a.t -= (this.offset - rad);
22667                 a.l += 1;
22668                 a.w -= (this.offset + rad + 1);
22669                 a.h -= (this.offset + rad);
22670                 a.h += 1;
22671             }
22672         break;
22673     };
22674
22675     this.adjusts = a;
22676 };
22677
22678 Roo.Shadow.prototype = {
22679     /**
22680      * @cfg {String} mode
22681      * The shadow display mode.  Supports the following options:<br />
22682      * sides: Shadow displays on both sides and bottom only<br />
22683      * frame: Shadow displays equally on all four sides<br />
22684      * drop: Traditional bottom-right drop shadow (default)
22685      */
22686     /**
22687      * @cfg {String} offset
22688      * The number of pixels to offset the shadow from the element (defaults to 4)
22689      */
22690     offset: 4,
22691
22692     // private
22693     defaultMode: "drop",
22694
22695     /**
22696      * Displays the shadow under the target element
22697      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22698      */
22699     show : function(target){
22700         target = Roo.get(target);
22701         if(!this.el){
22702             this.el = Roo.Shadow.Pool.pull();
22703             if(this.el.dom.nextSibling != target.dom){
22704                 this.el.insertBefore(target);
22705             }
22706         }
22707         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22708         if(Roo.isIE){
22709             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22710         }
22711         this.realign(
22712             target.getLeft(true),
22713             target.getTop(true),
22714             target.getWidth(),
22715             target.getHeight()
22716         );
22717         this.el.dom.style.display = "block";
22718     },
22719
22720     /**
22721      * Returns true if the shadow is visible, else false
22722      */
22723     isVisible : function(){
22724         return this.el ? true : false;  
22725     },
22726
22727     /**
22728      * Direct alignment when values are already available. Show must be called at least once before
22729      * calling this method to ensure it is initialized.
22730      * @param {Number} left The target element left position
22731      * @param {Number} top The target element top position
22732      * @param {Number} width The target element width
22733      * @param {Number} height The target element height
22734      */
22735     realign : function(l, t, w, h){
22736         if(!this.el){
22737             return;
22738         }
22739         var a = this.adjusts, d = this.el.dom, s = d.style;
22740         var iea = 0;
22741         s.left = (l+a.l)+"px";
22742         s.top = (t+a.t)+"px";
22743         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22744  
22745         if(s.width != sws || s.height != shs){
22746             s.width = sws;
22747             s.height = shs;
22748             if(!Roo.isIE){
22749                 var cn = d.childNodes;
22750                 var sww = Math.max(0, (sw-12))+"px";
22751                 cn[0].childNodes[1].style.width = sww;
22752                 cn[1].childNodes[1].style.width = sww;
22753                 cn[2].childNodes[1].style.width = sww;
22754                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22755             }
22756         }
22757     },
22758
22759     /**
22760      * Hides this shadow
22761      */
22762     hide : function(){
22763         if(this.el){
22764             this.el.dom.style.display = "none";
22765             Roo.Shadow.Pool.push(this.el);
22766             delete this.el;
22767         }
22768     },
22769
22770     /**
22771      * Adjust the z-index of this shadow
22772      * @param {Number} zindex The new z-index
22773      */
22774     setZIndex : function(z){
22775         this.zIndex = z;
22776         if(this.el){
22777             this.el.setStyle("z-index", z);
22778         }
22779     }
22780 };
22781
22782 // Private utility class that manages the internal Shadow cache
22783 Roo.Shadow.Pool = function(){
22784     var p = [];
22785     var markup = Roo.isIE ?
22786                  '<div class="x-ie-shadow"></div>' :
22787                  '<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>';
22788     return {
22789         pull : function(){
22790             var sh = p.shift();
22791             if(!sh){
22792                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22793                 sh.autoBoxAdjust = false;
22794             }
22795             return sh;
22796         },
22797
22798         push : function(sh){
22799             p.push(sh);
22800         }
22801     };
22802 }();/*
22803  * Based on:
22804  * Ext JS Library 1.1.1
22805  * Copyright(c) 2006-2007, Ext JS, LLC.
22806  *
22807  * Originally Released Under LGPL - original licence link has changed is not relivant.
22808  *
22809  * Fork - LGPL
22810  * <script type="text/javascript">
22811  */
22812
22813 /**
22814  * @class Roo.BoxComponent
22815  * @extends Roo.Component
22816  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22817  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22818  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22819  * layout containers.
22820  * @constructor
22821  * @param {Roo.Element/String/Object} config The configuration options.
22822  */
22823 Roo.BoxComponent = function(config){
22824     Roo.Component.call(this, config);
22825     this.addEvents({
22826         /**
22827          * @event resize
22828          * Fires after the component is resized.
22829              * @param {Roo.Component} this
22830              * @param {Number} adjWidth The box-adjusted width that was set
22831              * @param {Number} adjHeight The box-adjusted height that was set
22832              * @param {Number} rawWidth The width that was originally specified
22833              * @param {Number} rawHeight The height that was originally specified
22834              */
22835         resize : true,
22836         /**
22837          * @event move
22838          * Fires after the component is moved.
22839              * @param {Roo.Component} this
22840              * @param {Number} x The new x position
22841              * @param {Number} y The new y position
22842              */
22843         move : true
22844     });
22845 };
22846
22847 Roo.extend(Roo.BoxComponent, Roo.Component, {
22848     // private, set in afterRender to signify that the component has been rendered
22849     boxReady : false,
22850     // private, used to defer height settings to subclasses
22851     deferHeight: false,
22852     /** @cfg {Number} width
22853      * width (optional) size of component
22854      */
22855      /** @cfg {Number} height
22856      * height (optional) size of component
22857      */
22858      
22859     /**
22860      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22861      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22862      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22863      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22864      * @return {Roo.BoxComponent} this
22865      */
22866     setSize : function(w, h){
22867         // support for standard size objects
22868         if(typeof w == 'object'){
22869             h = w.height;
22870             w = w.width;
22871         }
22872         // not rendered
22873         if(!this.boxReady){
22874             this.width = w;
22875             this.height = h;
22876             return this;
22877         }
22878
22879         // prevent recalcs when not needed
22880         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22881             return this;
22882         }
22883         this.lastSize = {width: w, height: h};
22884
22885         var adj = this.adjustSize(w, h);
22886         var aw = adj.width, ah = adj.height;
22887         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22888             var rz = this.getResizeEl();
22889             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22890                 rz.setSize(aw, ah);
22891             }else if(!this.deferHeight && ah !== undefined){
22892                 rz.setHeight(ah);
22893             }else if(aw !== undefined){
22894                 rz.setWidth(aw);
22895             }
22896             this.onResize(aw, ah, w, h);
22897             this.fireEvent('resize', this, aw, ah, w, h);
22898         }
22899         return this;
22900     },
22901
22902     /**
22903      * Gets the current size of the component's underlying element.
22904      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22905      */
22906     getSize : function(){
22907         return this.el.getSize();
22908     },
22909
22910     /**
22911      * Gets the current XY position of the component's underlying element.
22912      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22913      * @return {Array} The XY position of the element (e.g., [100, 200])
22914      */
22915     getPosition : function(local){
22916         if(local === true){
22917             return [this.el.getLeft(true), this.el.getTop(true)];
22918         }
22919         return this.xy || this.el.getXY();
22920     },
22921
22922     /**
22923      * Gets the current box measurements of the component's underlying element.
22924      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22925      * @returns {Object} box An object in the format {x, y, width, height}
22926      */
22927     getBox : function(local){
22928         var s = this.el.getSize();
22929         if(local){
22930             s.x = this.el.getLeft(true);
22931             s.y = this.el.getTop(true);
22932         }else{
22933             var xy = this.xy || this.el.getXY();
22934             s.x = xy[0];
22935             s.y = xy[1];
22936         }
22937         return s;
22938     },
22939
22940     /**
22941      * Sets the current box measurements of the component's underlying element.
22942      * @param {Object} box An object in the format {x, y, width, height}
22943      * @returns {Roo.BoxComponent} this
22944      */
22945     updateBox : function(box){
22946         this.setSize(box.width, box.height);
22947         this.setPagePosition(box.x, box.y);
22948         return this;
22949     },
22950
22951     // protected
22952     getResizeEl : function(){
22953         return this.resizeEl || this.el;
22954     },
22955
22956     // protected
22957     getPositionEl : function(){
22958         return this.positionEl || this.el;
22959     },
22960
22961     /**
22962      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22963      * This method fires the move event.
22964      * @param {Number} left The new left
22965      * @param {Number} top The new top
22966      * @returns {Roo.BoxComponent} this
22967      */
22968     setPosition : function(x, y){
22969         this.x = x;
22970         this.y = y;
22971         if(!this.boxReady){
22972             return this;
22973         }
22974         var adj = this.adjustPosition(x, y);
22975         var ax = adj.x, ay = adj.y;
22976
22977         var el = this.getPositionEl();
22978         if(ax !== undefined || ay !== undefined){
22979             if(ax !== undefined && ay !== undefined){
22980                 el.setLeftTop(ax, ay);
22981             }else if(ax !== undefined){
22982                 el.setLeft(ax);
22983             }else if(ay !== undefined){
22984                 el.setTop(ay);
22985             }
22986             this.onPosition(ax, ay);
22987             this.fireEvent('move', this, ax, ay);
22988         }
22989         return this;
22990     },
22991
22992     /**
22993      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22994      * This method fires the move event.
22995      * @param {Number} x The new x position
22996      * @param {Number} y The new y position
22997      * @returns {Roo.BoxComponent} this
22998      */
22999     setPagePosition : function(x, y){
23000         this.pageX = x;
23001         this.pageY = y;
23002         if(!this.boxReady){
23003             return;
23004         }
23005         if(x === undefined || y === undefined){ // cannot translate undefined points
23006             return;
23007         }
23008         var p = this.el.translatePoints(x, y);
23009         this.setPosition(p.left, p.top);
23010         return this;
23011     },
23012
23013     // private
23014     onRender : function(ct, position){
23015         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23016         if(this.resizeEl){
23017             this.resizeEl = Roo.get(this.resizeEl);
23018         }
23019         if(this.positionEl){
23020             this.positionEl = Roo.get(this.positionEl);
23021         }
23022     },
23023
23024     // private
23025     afterRender : function(){
23026         Roo.BoxComponent.superclass.afterRender.call(this);
23027         this.boxReady = true;
23028         this.setSize(this.width, this.height);
23029         if(this.x || this.y){
23030             this.setPosition(this.x, this.y);
23031         }
23032         if(this.pageX || this.pageY){
23033             this.setPagePosition(this.pageX, this.pageY);
23034         }
23035     },
23036
23037     /**
23038      * Force the component's size to recalculate based on the underlying element's current height and width.
23039      * @returns {Roo.BoxComponent} this
23040      */
23041     syncSize : function(){
23042         delete this.lastSize;
23043         this.setSize(this.el.getWidth(), this.el.getHeight());
23044         return this;
23045     },
23046
23047     /**
23048      * Called after the component is resized, this method is empty by default but can be implemented by any
23049      * subclass that needs to perform custom logic after a resize occurs.
23050      * @param {Number} adjWidth The box-adjusted width that was set
23051      * @param {Number} adjHeight The box-adjusted height that was set
23052      * @param {Number} rawWidth The width that was originally specified
23053      * @param {Number} rawHeight The height that was originally specified
23054      */
23055     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23056
23057     },
23058
23059     /**
23060      * Called after the component is moved, this method is empty by default but can be implemented by any
23061      * subclass that needs to perform custom logic after a move occurs.
23062      * @param {Number} x The new x position
23063      * @param {Number} y The new y position
23064      */
23065     onPosition : function(x, y){
23066
23067     },
23068
23069     // private
23070     adjustSize : function(w, h){
23071         if(this.autoWidth){
23072             w = 'auto';
23073         }
23074         if(this.autoHeight){
23075             h = 'auto';
23076         }
23077         return {width : w, height: h};
23078     },
23079
23080     // private
23081     adjustPosition : function(x, y){
23082         return {x : x, y: y};
23083     }
23084 });/*
23085  * Based on:
23086  * Ext JS Library 1.1.1
23087  * Copyright(c) 2006-2007, Ext JS, LLC.
23088  *
23089  * Originally Released Under LGPL - original licence link has changed is not relivant.
23090  *
23091  * Fork - LGPL
23092  * <script type="text/javascript">
23093  */
23094
23095
23096 /**
23097  * @class Roo.SplitBar
23098  * @extends Roo.util.Observable
23099  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23100  * <br><br>
23101  * Usage:
23102  * <pre><code>
23103 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23104                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23105 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23106 split.minSize = 100;
23107 split.maxSize = 600;
23108 split.animate = true;
23109 split.on('moved', splitterMoved);
23110 </code></pre>
23111  * @constructor
23112  * Create a new SplitBar
23113  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23114  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23115  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23116  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23117                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23118                         position of the SplitBar).
23119  */
23120 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23121     
23122     /** @private */
23123     this.el = Roo.get(dragElement, true);
23124     this.el.dom.unselectable = "on";
23125     /** @private */
23126     this.resizingEl = Roo.get(resizingElement, true);
23127
23128     /**
23129      * @private
23130      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23131      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23132      * @type Number
23133      */
23134     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23135     
23136     /**
23137      * The minimum size of the resizing element. (Defaults to 0)
23138      * @type Number
23139      */
23140     this.minSize = 0;
23141     
23142     /**
23143      * The maximum size of the resizing element. (Defaults to 2000)
23144      * @type Number
23145      */
23146     this.maxSize = 2000;
23147     
23148     /**
23149      * Whether to animate the transition to the new size
23150      * @type Boolean
23151      */
23152     this.animate = false;
23153     
23154     /**
23155      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23156      * @type Boolean
23157      */
23158     this.useShim = false;
23159     
23160     /** @private */
23161     this.shim = null;
23162     
23163     if(!existingProxy){
23164         /** @private */
23165         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23166     }else{
23167         this.proxy = Roo.get(existingProxy).dom;
23168     }
23169     /** @private */
23170     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23171     
23172     /** @private */
23173     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23174     
23175     /** @private */
23176     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23177     
23178     /** @private */
23179     this.dragSpecs = {};
23180     
23181     /**
23182      * @private The adapter to use to positon and resize elements
23183      */
23184     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23185     this.adapter.init(this);
23186     
23187     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23188         /** @private */
23189         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23190         this.el.addClass("x-splitbar-h");
23191     }else{
23192         /** @private */
23193         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23194         this.el.addClass("x-splitbar-v");
23195     }
23196     
23197     this.addEvents({
23198         /**
23199          * @event resize
23200          * Fires when the splitter is moved (alias for {@link #event-moved})
23201          * @param {Roo.SplitBar} this
23202          * @param {Number} newSize the new width or height
23203          */
23204         "resize" : true,
23205         /**
23206          * @event moved
23207          * Fires when the splitter is moved
23208          * @param {Roo.SplitBar} this
23209          * @param {Number} newSize the new width or height
23210          */
23211         "moved" : true,
23212         /**
23213          * @event beforeresize
23214          * Fires before the splitter is dragged
23215          * @param {Roo.SplitBar} this
23216          */
23217         "beforeresize" : true,
23218
23219         "beforeapply" : true
23220     });
23221
23222     Roo.util.Observable.call(this);
23223 };
23224
23225 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23226     onStartProxyDrag : function(x, y){
23227         this.fireEvent("beforeresize", this);
23228         if(!this.overlay){
23229             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23230             o.unselectable();
23231             o.enableDisplayMode("block");
23232             // all splitbars share the same overlay
23233             Roo.SplitBar.prototype.overlay = o;
23234         }
23235         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23236         this.overlay.show();
23237         Roo.get(this.proxy).setDisplayed("block");
23238         var size = this.adapter.getElementSize(this);
23239         this.activeMinSize = this.getMinimumSize();;
23240         this.activeMaxSize = this.getMaximumSize();;
23241         var c1 = size - this.activeMinSize;
23242         var c2 = Math.max(this.activeMaxSize - size, 0);
23243         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23244             this.dd.resetConstraints();
23245             this.dd.setXConstraint(
23246                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23247                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23248             );
23249             this.dd.setYConstraint(0, 0);
23250         }else{
23251             this.dd.resetConstraints();
23252             this.dd.setXConstraint(0, 0);
23253             this.dd.setYConstraint(
23254                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23255                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23256             );
23257          }
23258         this.dragSpecs.startSize = size;
23259         this.dragSpecs.startPoint = [x, y];
23260         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23261     },
23262     
23263     /** 
23264      * @private Called after the drag operation by the DDProxy
23265      */
23266     onEndProxyDrag : function(e){
23267         Roo.get(this.proxy).setDisplayed(false);
23268         var endPoint = Roo.lib.Event.getXY(e);
23269         if(this.overlay){
23270             this.overlay.hide();
23271         }
23272         var newSize;
23273         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23274             newSize = this.dragSpecs.startSize + 
23275                 (this.placement == Roo.SplitBar.LEFT ?
23276                     endPoint[0] - this.dragSpecs.startPoint[0] :
23277                     this.dragSpecs.startPoint[0] - endPoint[0]
23278                 );
23279         }else{
23280             newSize = this.dragSpecs.startSize + 
23281                 (this.placement == Roo.SplitBar.TOP ?
23282                     endPoint[1] - this.dragSpecs.startPoint[1] :
23283                     this.dragSpecs.startPoint[1] - endPoint[1]
23284                 );
23285         }
23286         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23287         if(newSize != this.dragSpecs.startSize){
23288             if(this.fireEvent('beforeapply', this, newSize) !== false){
23289                 this.adapter.setElementSize(this, newSize);
23290                 this.fireEvent("moved", this, newSize);
23291                 this.fireEvent("resize", this, newSize);
23292             }
23293         }
23294     },
23295     
23296     /**
23297      * Get the adapter this SplitBar uses
23298      * @return The adapter object
23299      */
23300     getAdapter : function(){
23301         return this.adapter;
23302     },
23303     
23304     /**
23305      * Set the adapter this SplitBar uses
23306      * @param {Object} adapter A SplitBar adapter object
23307      */
23308     setAdapter : function(adapter){
23309         this.adapter = adapter;
23310         this.adapter.init(this);
23311     },
23312     
23313     /**
23314      * Gets the minimum size for the resizing element
23315      * @return {Number} The minimum size
23316      */
23317     getMinimumSize : function(){
23318         return this.minSize;
23319     },
23320     
23321     /**
23322      * Sets the minimum size for the resizing element
23323      * @param {Number} minSize The minimum size
23324      */
23325     setMinimumSize : function(minSize){
23326         this.minSize = minSize;
23327     },
23328     
23329     /**
23330      * Gets the maximum size for the resizing element
23331      * @return {Number} The maximum size
23332      */
23333     getMaximumSize : function(){
23334         return this.maxSize;
23335     },
23336     
23337     /**
23338      * Sets the maximum size for the resizing element
23339      * @param {Number} maxSize The maximum size
23340      */
23341     setMaximumSize : function(maxSize){
23342         this.maxSize = maxSize;
23343     },
23344     
23345     /**
23346      * Sets the initialize size for the resizing element
23347      * @param {Number} size The initial size
23348      */
23349     setCurrentSize : function(size){
23350         var oldAnimate = this.animate;
23351         this.animate = false;
23352         this.adapter.setElementSize(this, size);
23353         this.animate = oldAnimate;
23354     },
23355     
23356     /**
23357      * Destroy this splitbar. 
23358      * @param {Boolean} removeEl True to remove the element
23359      */
23360     destroy : function(removeEl){
23361         if(this.shim){
23362             this.shim.remove();
23363         }
23364         this.dd.unreg();
23365         this.proxy.parentNode.removeChild(this.proxy);
23366         if(removeEl){
23367             this.el.remove();
23368         }
23369     }
23370 });
23371
23372 /**
23373  * @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.
23374  */
23375 Roo.SplitBar.createProxy = function(dir){
23376     var proxy = new Roo.Element(document.createElement("div"));
23377     proxy.unselectable();
23378     var cls = 'x-splitbar-proxy';
23379     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23380     document.body.appendChild(proxy.dom);
23381     return proxy.dom;
23382 };
23383
23384 /** 
23385  * @class Roo.SplitBar.BasicLayoutAdapter
23386  * Default Adapter. It assumes the splitter and resizing element are not positioned
23387  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23388  */
23389 Roo.SplitBar.BasicLayoutAdapter = function(){
23390 };
23391
23392 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23393     // do nothing for now
23394     init : function(s){
23395     
23396     },
23397     /**
23398      * Called before drag operations to get the current size of the resizing element. 
23399      * @param {Roo.SplitBar} s The SplitBar using this adapter
23400      */
23401      getElementSize : function(s){
23402         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23403             return s.resizingEl.getWidth();
23404         }else{
23405             return s.resizingEl.getHeight();
23406         }
23407     },
23408     
23409     /**
23410      * Called after drag operations to set the size of the resizing element.
23411      * @param {Roo.SplitBar} s The SplitBar using this adapter
23412      * @param {Number} newSize The new size to set
23413      * @param {Function} onComplete A function to be invoked when resizing is complete
23414      */
23415     setElementSize : function(s, newSize, onComplete){
23416         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23417             if(!s.animate){
23418                 s.resizingEl.setWidth(newSize);
23419                 if(onComplete){
23420                     onComplete(s, newSize);
23421                 }
23422             }else{
23423                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23424             }
23425         }else{
23426             
23427             if(!s.animate){
23428                 s.resizingEl.setHeight(newSize);
23429                 if(onComplete){
23430                     onComplete(s, newSize);
23431                 }
23432             }else{
23433                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23434             }
23435         }
23436     }
23437 };
23438
23439 /** 
23440  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23441  * @extends Roo.SplitBar.BasicLayoutAdapter
23442  * Adapter that  moves the splitter element to align with the resized sizing element. 
23443  * Used with an absolute positioned SplitBar.
23444  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23445  * document.body, make sure you assign an id to the body element.
23446  */
23447 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23448     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23449     this.container = Roo.get(container);
23450 };
23451
23452 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23453     init : function(s){
23454         this.basic.init(s);
23455     },
23456     
23457     getElementSize : function(s){
23458         return this.basic.getElementSize(s);
23459     },
23460     
23461     setElementSize : function(s, newSize, onComplete){
23462         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23463     },
23464     
23465     moveSplitter : function(s){
23466         var yes = Roo.SplitBar;
23467         switch(s.placement){
23468             case yes.LEFT:
23469                 s.el.setX(s.resizingEl.getRight());
23470                 break;
23471             case yes.RIGHT:
23472                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23473                 break;
23474             case yes.TOP:
23475                 s.el.setY(s.resizingEl.getBottom());
23476                 break;
23477             case yes.BOTTOM:
23478                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23479                 break;
23480         }
23481     }
23482 };
23483
23484 /**
23485  * Orientation constant - Create a vertical SplitBar
23486  * @static
23487  * @type Number
23488  */
23489 Roo.SplitBar.VERTICAL = 1;
23490
23491 /**
23492  * Orientation constant - Create a horizontal SplitBar
23493  * @static
23494  * @type Number
23495  */
23496 Roo.SplitBar.HORIZONTAL = 2;
23497
23498 /**
23499  * Placement constant - The resizing element is to the left of the splitter element
23500  * @static
23501  * @type Number
23502  */
23503 Roo.SplitBar.LEFT = 1;
23504
23505 /**
23506  * Placement constant - The resizing element is to the right of the splitter element
23507  * @static
23508  * @type Number
23509  */
23510 Roo.SplitBar.RIGHT = 2;
23511
23512 /**
23513  * Placement constant - The resizing element is positioned above the splitter element
23514  * @static
23515  * @type Number
23516  */
23517 Roo.SplitBar.TOP = 3;
23518
23519 /**
23520  * Placement constant - The resizing element is positioned under splitter element
23521  * @static
23522  * @type Number
23523  */
23524 Roo.SplitBar.BOTTOM = 4;
23525 /*
23526  * Based on:
23527  * Ext JS Library 1.1.1
23528  * Copyright(c) 2006-2007, Ext JS, LLC.
23529  *
23530  * Originally Released Under LGPL - original licence link has changed is not relivant.
23531  *
23532  * Fork - LGPL
23533  * <script type="text/javascript">
23534  */
23535
23536 /**
23537  * @class Roo.View
23538  * @extends Roo.util.Observable
23539  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23540  * This class also supports single and multi selection modes. <br>
23541  * Create a data model bound view:
23542  <pre><code>
23543  var store = new Roo.data.Store(...);
23544
23545  var view = new Roo.View({
23546     el : "my-element",
23547     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23548  
23549     singleSelect: true,
23550     selectedClass: "ydataview-selected",
23551     store: store
23552  });
23553
23554  // listen for node click?
23555  view.on("click", function(vw, index, node, e){
23556  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23557  });
23558
23559  // load XML data
23560  dataModel.load("foobar.xml");
23561  </code></pre>
23562  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23563  * <br><br>
23564  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23565  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23566  * 
23567  * Note: old style constructor is still suported (container, template, config)
23568  * 
23569  * @constructor
23570  * Create a new View
23571  * @param {Object} config The config object
23572  * 
23573  */
23574 Roo.View = function(config, depreciated_tpl, depreciated_config){
23575     
23576     if (typeof(depreciated_tpl) == 'undefined') {
23577         // new way.. - universal constructor.
23578         Roo.apply(this, config);
23579         this.el  = Roo.get(this.el);
23580     } else {
23581         // old format..
23582         this.el  = Roo.get(config);
23583         this.tpl = depreciated_tpl;
23584         Roo.apply(this, depreciated_config);
23585     }
23586      
23587     
23588     if(typeof(this.tpl) == "string"){
23589         this.tpl = new Roo.Template(this.tpl);
23590     } else {
23591         // support xtype ctors..
23592         this.tpl = new Roo.factory(this.tpl, Roo);
23593     }
23594     
23595     
23596     this.tpl.compile();
23597    
23598
23599      
23600     /** @private */
23601     this.addEvents({
23602     /**
23603      * @event beforeclick
23604      * Fires before a click is processed. Returns false to cancel the default action.
23605      * @param {Roo.View} this
23606      * @param {Number} index The index of the target node
23607      * @param {HTMLElement} node The target node
23608      * @param {Roo.EventObject} e The raw event object
23609      */
23610         "beforeclick" : true,
23611     /**
23612      * @event click
23613      * Fires when a template node is clicked.
23614      * @param {Roo.View} this
23615      * @param {Number} index The index of the target node
23616      * @param {HTMLElement} node The target node
23617      * @param {Roo.EventObject} e The raw event object
23618      */
23619         "click" : true,
23620     /**
23621      * @event dblclick
23622      * Fires when a template node is double clicked.
23623      * @param {Roo.View} this
23624      * @param {Number} index The index of the target node
23625      * @param {HTMLElement} node The target node
23626      * @param {Roo.EventObject} e The raw event object
23627      */
23628         "dblclick" : true,
23629     /**
23630      * @event contextmenu
23631      * Fires when a template node is right clicked.
23632      * @param {Roo.View} this
23633      * @param {Number} index The index of the target node
23634      * @param {HTMLElement} node The target node
23635      * @param {Roo.EventObject} e The raw event object
23636      */
23637         "contextmenu" : true,
23638     /**
23639      * @event selectionchange
23640      * Fires when the selected nodes change.
23641      * @param {Roo.View} this
23642      * @param {Array} selections Array of the selected nodes
23643      */
23644         "selectionchange" : true,
23645
23646     /**
23647      * @event beforeselect
23648      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23649      * @param {Roo.View} this
23650      * @param {HTMLElement} node The node to be selected
23651      * @param {Array} selections Array of currently selected nodes
23652      */
23653         "beforeselect" : true
23654     });
23655
23656     this.el.on({
23657         "click": this.onClick,
23658         "dblclick": this.onDblClick,
23659         "contextmenu": this.onContextMenu,
23660         scope:this
23661     });
23662
23663     this.selections = [];
23664     this.nodes = [];
23665     this.cmp = new Roo.CompositeElementLite([]);
23666     if(this.store){
23667         this.store = Roo.factory(this.store, Roo.data);
23668         this.setStore(this.store, true);
23669     }
23670     Roo.View.superclass.constructor.call(this);
23671 };
23672
23673 Roo.extend(Roo.View, Roo.util.Observable, {
23674     
23675      /**
23676      * @cfg {Roo.data.Store} store Data store to load data from.
23677      */
23678     store : false,
23679     
23680     /**
23681      * @cfg {String|Roo.Element} el The container element.
23682      */
23683     el : '',
23684     
23685     /**
23686      * @cfg {String|Roo.Template} tpl The template used by this View 
23687      */
23688     tpl : false,
23689     
23690     /**
23691      * @cfg {String} selectedClass The css class to add to selected nodes
23692      */
23693     selectedClass : "x-view-selected",
23694      /**
23695      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23696      */
23697     emptyText : "",
23698     /**
23699      * @cfg {Boolean} multiSelect Allow multiple selection
23700      */
23701     
23702     multiSelect : false,
23703     /**
23704      * @cfg {Boolean} singleSelect Allow single selection
23705      */
23706     singleSelect:  false,
23707     
23708     /**
23709      * Returns the element this view is bound to.
23710      * @return {Roo.Element}
23711      */
23712     getEl : function(){
23713         return this.el;
23714     },
23715
23716     /**
23717      * Refreshes the view.
23718      */
23719     refresh : function(){
23720         var t = this.tpl;
23721         this.clearSelections();
23722         this.el.update("");
23723         var html = [];
23724         var records = this.store.getRange();
23725         if(records.length < 1){
23726             this.el.update(this.emptyText);
23727             return;
23728         }
23729         for(var i = 0, len = records.length; i < len; i++){
23730             var data = this.prepareData(records[i].data, i, records[i]);
23731             html[html.length] = t.apply(data);
23732         }
23733         this.el.update(html.join(""));
23734         this.nodes = this.el.dom.childNodes;
23735         this.updateIndexes(0);
23736     },
23737
23738     /**
23739      * Function to override to reformat the data that is sent to
23740      * the template for each node.
23741      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23742      * a JSON object for an UpdateManager bound view).
23743      */
23744     prepareData : function(data){
23745         return data;
23746     },
23747
23748     onUpdate : function(ds, record){
23749         this.clearSelections();
23750         var index = this.store.indexOf(record);
23751         var n = this.nodes[index];
23752         this.tpl.insertBefore(n, this.prepareData(record.data));
23753         n.parentNode.removeChild(n);
23754         this.updateIndexes(index, index);
23755     },
23756
23757     onAdd : function(ds, records, index){
23758         this.clearSelections();
23759         if(this.nodes.length == 0){
23760             this.refresh();
23761             return;
23762         }
23763         var n = this.nodes[index];
23764         for(var i = 0, len = records.length; i < len; i++){
23765             var d = this.prepareData(records[i].data);
23766             if(n){
23767                 this.tpl.insertBefore(n, d);
23768             }else{
23769                 this.tpl.append(this.el, d);
23770             }
23771         }
23772         this.updateIndexes(index);
23773     },
23774
23775     onRemove : function(ds, record, index){
23776         this.clearSelections();
23777         this.el.dom.removeChild(this.nodes[index]);
23778         this.updateIndexes(index);
23779     },
23780
23781     /**
23782      * Refresh an individual node.
23783      * @param {Number} index
23784      */
23785     refreshNode : function(index){
23786         this.onUpdate(this.store, this.store.getAt(index));
23787     },
23788
23789     updateIndexes : function(startIndex, endIndex){
23790         var ns = this.nodes;
23791         startIndex = startIndex || 0;
23792         endIndex = endIndex || ns.length - 1;
23793         for(var i = startIndex; i <= endIndex; i++){
23794             ns[i].nodeIndex = i;
23795         }
23796     },
23797
23798     /**
23799      * Changes the data store this view uses and refresh the view.
23800      * @param {Store} store
23801      */
23802     setStore : function(store, initial){
23803         if(!initial && this.store){
23804             this.store.un("datachanged", this.refresh);
23805             this.store.un("add", this.onAdd);
23806             this.store.un("remove", this.onRemove);
23807             this.store.un("update", this.onUpdate);
23808             this.store.un("clear", this.refresh);
23809         }
23810         if(store){
23811           
23812             store.on("datachanged", this.refresh, this);
23813             store.on("add", this.onAdd, this);
23814             store.on("remove", this.onRemove, this);
23815             store.on("update", this.onUpdate, this);
23816             store.on("clear", this.refresh, this);
23817         }
23818         
23819         if(store){
23820             this.refresh();
23821         }
23822     },
23823
23824     /**
23825      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23826      * @param {HTMLElement} node
23827      * @return {HTMLElement} The template node
23828      */
23829     findItemFromChild : function(node){
23830         var el = this.el.dom;
23831         if(!node || node.parentNode == el){
23832                     return node;
23833             }
23834             var p = node.parentNode;
23835             while(p && p != el){
23836             if(p.parentNode == el){
23837                 return p;
23838             }
23839             p = p.parentNode;
23840         }
23841             return null;
23842     },
23843
23844     /** @ignore */
23845     onClick : function(e){
23846         var item = this.findItemFromChild(e.getTarget());
23847         if(item){
23848             var index = this.indexOf(item);
23849             if(this.onItemClick(item, index, e) !== false){
23850                 this.fireEvent("click", this, index, item, e);
23851             }
23852         }else{
23853             this.clearSelections();
23854         }
23855     },
23856
23857     /** @ignore */
23858     onContextMenu : function(e){
23859         var item = this.findItemFromChild(e.getTarget());
23860         if(item){
23861             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23862         }
23863     },
23864
23865     /** @ignore */
23866     onDblClick : function(e){
23867         var item = this.findItemFromChild(e.getTarget());
23868         if(item){
23869             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23870         }
23871     },
23872
23873     onItemClick : function(item, index, e){
23874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23875             return false;
23876         }
23877         if(this.multiSelect || this.singleSelect){
23878             if(this.multiSelect && e.shiftKey && this.lastSelection){
23879                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23880             }else{
23881                 this.select(item, this.multiSelect && e.ctrlKey);
23882                 this.lastSelection = item;
23883             }
23884             e.preventDefault();
23885         }
23886         return true;
23887     },
23888
23889     /**
23890      * Get the number of selected nodes.
23891      * @return {Number}
23892      */
23893     getSelectionCount : function(){
23894         return this.selections.length;
23895     },
23896
23897     /**
23898      * Get the currently selected nodes.
23899      * @return {Array} An array of HTMLElements
23900      */
23901     getSelectedNodes : function(){
23902         return this.selections;
23903     },
23904
23905     /**
23906      * Get the indexes of the selected nodes.
23907      * @return {Array}
23908      */
23909     getSelectedIndexes : function(){
23910         var indexes = [], s = this.selections;
23911         for(var i = 0, len = s.length; i < len; i++){
23912             indexes.push(s[i].nodeIndex);
23913         }
23914         return indexes;
23915     },
23916
23917     /**
23918      * Clear all selections
23919      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23920      */
23921     clearSelections : function(suppressEvent){
23922         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23923             this.cmp.elements = this.selections;
23924             this.cmp.removeClass(this.selectedClass);
23925             this.selections = [];
23926             if(!suppressEvent){
23927                 this.fireEvent("selectionchange", this, this.selections);
23928             }
23929         }
23930     },
23931
23932     /**
23933      * Returns true if the passed node is selected
23934      * @param {HTMLElement/Number} node The node or node index
23935      * @return {Boolean}
23936      */
23937     isSelected : function(node){
23938         var s = this.selections;
23939         if(s.length < 1){
23940             return false;
23941         }
23942         node = this.getNode(node);
23943         return s.indexOf(node) !== -1;
23944     },
23945
23946     /**
23947      * Selects nodes.
23948      * @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
23949      * @param {Boolean} keepExisting (optional) true to keep existing selections
23950      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23951      */
23952     select : function(nodeInfo, keepExisting, suppressEvent){
23953         if(nodeInfo instanceof Array){
23954             if(!keepExisting){
23955                 this.clearSelections(true);
23956             }
23957             for(var i = 0, len = nodeInfo.length; i < len; i++){
23958                 this.select(nodeInfo[i], true, true);
23959             }
23960         } else{
23961             var node = this.getNode(nodeInfo);
23962             if(node && !this.isSelected(node)){
23963                 if(!keepExisting){
23964                     this.clearSelections(true);
23965                 }
23966                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23967                     Roo.fly(node).addClass(this.selectedClass);
23968                     this.selections.push(node);
23969                     if(!suppressEvent){
23970                         this.fireEvent("selectionchange", this, this.selections);
23971                     }
23972                 }
23973             }
23974         }
23975     },
23976
23977     /**
23978      * Gets a template node.
23979      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23980      * @return {HTMLElement} The node or null if it wasn't found
23981      */
23982     getNode : function(nodeInfo){
23983         if(typeof nodeInfo == "string"){
23984             return document.getElementById(nodeInfo);
23985         }else if(typeof nodeInfo == "number"){
23986             return this.nodes[nodeInfo];
23987         }
23988         return nodeInfo;
23989     },
23990
23991     /**
23992      * Gets a range template nodes.
23993      * @param {Number} startIndex
23994      * @param {Number} endIndex
23995      * @return {Array} An array of nodes
23996      */
23997     getNodes : function(start, end){
23998         var ns = this.nodes;
23999         start = start || 0;
24000         end = typeof end == "undefined" ? ns.length - 1 : end;
24001         var nodes = [];
24002         if(start <= end){
24003             for(var i = start; i <= end; i++){
24004                 nodes.push(ns[i]);
24005             }
24006         } else{
24007             for(var i = start; i >= end; i--){
24008                 nodes.push(ns[i]);
24009             }
24010         }
24011         return nodes;
24012     },
24013
24014     /**
24015      * Finds the index of the passed node
24016      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24017      * @return {Number} The index of the node or -1
24018      */
24019     indexOf : function(node){
24020         node = this.getNode(node);
24021         if(typeof node.nodeIndex == "number"){
24022             return node.nodeIndex;
24023         }
24024         var ns = this.nodes;
24025         for(var i = 0, len = ns.length; i < len; i++){
24026             if(ns[i] == node){
24027                 return i;
24028             }
24029         }
24030         return -1;
24031     }
24032 });
24033 /*
24034  * Based on:
24035  * Ext JS Library 1.1.1
24036  * Copyright(c) 2006-2007, Ext JS, LLC.
24037  *
24038  * Originally Released Under LGPL - original licence link has changed is not relivant.
24039  *
24040  * Fork - LGPL
24041  * <script type="text/javascript">
24042  */
24043
24044 /**
24045  * @class Roo.JsonView
24046  * @extends Roo.View
24047  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24048 <pre><code>
24049 var view = new Roo.JsonView({
24050     container: "my-element",
24051     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24052     multiSelect: true, 
24053     jsonRoot: "data" 
24054 });
24055
24056 // listen for node click?
24057 view.on("click", function(vw, index, node, e){
24058     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24059 });
24060
24061 // direct load of JSON data
24062 view.load("foobar.php");
24063
24064 // Example from my blog list
24065 var tpl = new Roo.Template(
24066     '&lt;div class="entry"&gt;' +
24067     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24068     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24069     "&lt;/div&gt;&lt;hr /&gt;"
24070 );
24071
24072 var moreView = new Roo.JsonView({
24073     container :  "entry-list", 
24074     template : tpl,
24075     jsonRoot: "posts"
24076 });
24077 moreView.on("beforerender", this.sortEntries, this);
24078 moreView.load({
24079     url: "/blog/get-posts.php",
24080     params: "allposts=true",
24081     text: "Loading Blog Entries..."
24082 });
24083 </code></pre>
24084
24085 * Note: old code is supported with arguments : (container, template, config)
24086
24087
24088  * @constructor
24089  * Create a new JsonView
24090  * 
24091  * @param {Object} config The config object
24092  * 
24093  */
24094 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24095     
24096     
24097     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24098
24099     var um = this.el.getUpdateManager();
24100     um.setRenderer(this);
24101     um.on("update", this.onLoad, this);
24102     um.on("failure", this.onLoadException, this);
24103
24104     /**
24105      * @event beforerender
24106      * Fires before rendering of the downloaded JSON data.
24107      * @param {Roo.JsonView} this
24108      * @param {Object} data The JSON data loaded
24109      */
24110     /**
24111      * @event load
24112      * Fires when data is loaded.
24113      * @param {Roo.JsonView} this
24114      * @param {Object} data The JSON data loaded
24115      * @param {Object} response The raw Connect response object
24116      */
24117     /**
24118      * @event loadexception
24119      * Fires when loading fails.
24120      * @param {Roo.JsonView} this
24121      * @param {Object} response The raw Connect response object
24122      */
24123     this.addEvents({
24124         'beforerender' : true,
24125         'load' : true,
24126         'loadexception' : true
24127     });
24128 };
24129 Roo.extend(Roo.JsonView, Roo.View, {
24130     /**
24131      * @type {String} The root property in the loaded JSON object that contains the data
24132      */
24133     jsonRoot : "",
24134
24135     /**
24136      * Refreshes the view.
24137      */
24138     refresh : function(){
24139         this.clearSelections();
24140         this.el.update("");
24141         var html = [];
24142         var o = this.jsonData;
24143         if(o && o.length > 0){
24144             for(var i = 0, len = o.length; i < len; i++){
24145                 var data = this.prepareData(o[i], i, o);
24146                 html[html.length] = this.tpl.apply(data);
24147             }
24148         }else{
24149             html.push(this.emptyText);
24150         }
24151         this.el.update(html.join(""));
24152         this.nodes = this.el.dom.childNodes;
24153         this.updateIndexes(0);
24154     },
24155
24156     /**
24157      * 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.
24158      * @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:
24159      <pre><code>
24160      view.load({
24161          url: "your-url.php",
24162          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24163          callback: yourFunction,
24164          scope: yourObject, //(optional scope)
24165          discardUrl: false,
24166          nocache: false,
24167          text: "Loading...",
24168          timeout: 30,
24169          scripts: false
24170      });
24171      </code></pre>
24172      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24173      * 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.
24174      * @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}
24175      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24176      * @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.
24177      */
24178     load : function(){
24179         var um = this.el.getUpdateManager();
24180         um.update.apply(um, arguments);
24181     },
24182
24183     render : function(el, response){
24184         this.clearSelections();
24185         this.el.update("");
24186         var o;
24187         try{
24188             o = Roo.util.JSON.decode(response.responseText);
24189             if(this.jsonRoot){
24190                 
24191                 o = o[this.jsonRoot];
24192             }
24193         } catch(e){
24194         }
24195         /**
24196          * The current JSON data or null
24197          */
24198         this.jsonData = o;
24199         this.beforeRender();
24200         this.refresh();
24201     },
24202
24203 /**
24204  * Get the number of records in the current JSON dataset
24205  * @return {Number}
24206  */
24207     getCount : function(){
24208         return this.jsonData ? this.jsonData.length : 0;
24209     },
24210
24211 /**
24212  * Returns the JSON object for the specified node(s)
24213  * @param {HTMLElement/Array} node The node or an array of nodes
24214  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24215  * you get the JSON object for the node
24216  */
24217     getNodeData : function(node){
24218         if(node instanceof Array){
24219             var data = [];
24220             for(var i = 0, len = node.length; i < len; i++){
24221                 data.push(this.getNodeData(node[i]));
24222             }
24223             return data;
24224         }
24225         return this.jsonData[this.indexOf(node)] || null;
24226     },
24227
24228     beforeRender : function(){
24229         this.snapshot = this.jsonData;
24230         if(this.sortInfo){
24231             this.sort.apply(this, this.sortInfo);
24232         }
24233         this.fireEvent("beforerender", this, this.jsonData);
24234     },
24235
24236     onLoad : function(el, o){
24237         this.fireEvent("load", this, this.jsonData, o);
24238     },
24239
24240     onLoadException : function(el, o){
24241         this.fireEvent("loadexception", this, o);
24242     },
24243
24244 /**
24245  * Filter the data by a specific property.
24246  * @param {String} property A property on your JSON objects
24247  * @param {String/RegExp} value Either string that the property values
24248  * should start with, or a RegExp to test against the property
24249  */
24250     filter : function(property, value){
24251         if(this.jsonData){
24252             var data = [];
24253             var ss = this.snapshot;
24254             if(typeof value == "string"){
24255                 var vlen = value.length;
24256                 if(vlen == 0){
24257                     this.clearFilter();
24258                     return;
24259                 }
24260                 value = value.toLowerCase();
24261                 for(var i = 0, len = ss.length; i < len; i++){
24262                     var o = ss[i];
24263                     if(o[property].substr(0, vlen).toLowerCase() == value){
24264                         data.push(o);
24265                     }
24266                 }
24267             } else if(value.exec){ // regex?
24268                 for(var i = 0, len = ss.length; i < len; i++){
24269                     var o = ss[i];
24270                     if(value.test(o[property])){
24271                         data.push(o);
24272                     }
24273                 }
24274             } else{
24275                 return;
24276             }
24277             this.jsonData = data;
24278             this.refresh();
24279         }
24280     },
24281
24282 /**
24283  * Filter by a function. The passed function will be called with each
24284  * object in the current dataset. If the function returns true the value is kept,
24285  * otherwise it is filtered.
24286  * @param {Function} fn
24287  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24288  */
24289     filterBy : function(fn, scope){
24290         if(this.jsonData){
24291             var data = [];
24292             var ss = this.snapshot;
24293             for(var i = 0, len = ss.length; i < len; i++){
24294                 var o = ss[i];
24295                 if(fn.call(scope || this, o)){
24296                     data.push(o);
24297                 }
24298             }
24299             this.jsonData = data;
24300             this.refresh();
24301         }
24302     },
24303
24304 /**
24305  * Clears the current filter.
24306  */
24307     clearFilter : function(){
24308         if(this.snapshot && this.jsonData != this.snapshot){
24309             this.jsonData = this.snapshot;
24310             this.refresh();
24311         }
24312     },
24313
24314
24315 /**
24316  * Sorts the data for this view and refreshes it.
24317  * @param {String} property A property on your JSON objects to sort on
24318  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24319  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24320  */
24321     sort : function(property, dir, sortType){
24322         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24323         if(this.jsonData){
24324             var p = property;
24325             var dsc = dir && dir.toLowerCase() == "desc";
24326             var f = function(o1, o2){
24327                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24328                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24329                 ;
24330                 if(v1 < v2){
24331                     return dsc ? +1 : -1;
24332                 } else if(v1 > v2){
24333                     return dsc ? -1 : +1;
24334                 } else{
24335                     return 0;
24336                 }
24337             };
24338             this.jsonData.sort(f);
24339             this.refresh();
24340             if(this.jsonData != this.snapshot){
24341                 this.snapshot.sort(f);
24342             }
24343         }
24344     }
24345 });/*
24346  * Based on:
24347  * Ext JS Library 1.1.1
24348  * Copyright(c) 2006-2007, Ext JS, LLC.
24349  *
24350  * Originally Released Under LGPL - original licence link has changed is not relivant.
24351  *
24352  * Fork - LGPL
24353  * <script type="text/javascript">
24354  */
24355  
24356
24357 /**
24358  * @class Roo.ColorPalette
24359  * @extends Roo.Component
24360  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24361  * Here's an example of typical usage:
24362  * <pre><code>
24363 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24364 cp.render('my-div');
24365
24366 cp.on('select', function(palette, selColor){
24367     // do something with selColor
24368 });
24369 </code></pre>
24370  * @constructor
24371  * Create a new ColorPalette
24372  * @param {Object} config The config object
24373  */
24374 Roo.ColorPalette = function(config){
24375     Roo.ColorPalette.superclass.constructor.call(this, config);
24376     this.addEvents({
24377         /**
24378              * @event select
24379              * Fires when a color is selected
24380              * @param {ColorPalette} this
24381              * @param {String} color The 6-digit color hex code (without the # symbol)
24382              */
24383         select: true
24384     });
24385
24386     if(this.handler){
24387         this.on("select", this.handler, this.scope, true);
24388     }
24389 };
24390 Roo.extend(Roo.ColorPalette, Roo.Component, {
24391     /**
24392      * @cfg {String} itemCls
24393      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24394      */
24395     itemCls : "x-color-palette",
24396     /**
24397      * @cfg {String} value
24398      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24399      * the hex codes are case-sensitive.
24400      */
24401     value : null,
24402     clickEvent:'click',
24403     // private
24404     ctype: "Roo.ColorPalette",
24405
24406     /**
24407      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24408      */
24409     allowReselect : false,
24410
24411     /**
24412      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24413      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24414      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24415      * of colors with the width setting until the box is symmetrical.</p>
24416      * <p>You can override individual colors if needed:</p>
24417      * <pre><code>
24418 var cp = new Roo.ColorPalette();
24419 cp.colors[0] = "FF0000";  // change the first box to red
24420 </code></pre>
24421
24422 Or you can provide a custom array of your own for complete control:
24423 <pre><code>
24424 var cp = new Roo.ColorPalette();
24425 cp.colors = ["000000", "993300", "333300"];
24426 </code></pre>
24427      * @type Array
24428      */
24429     colors : [
24430         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24431         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24432         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24433         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24434         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24435     ],
24436
24437     // private
24438     onRender : function(container, position){
24439         var t = new Roo.MasterTemplate(
24440             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24441         );
24442         var c = this.colors;
24443         for(var i = 0, len = c.length; i < len; i++){
24444             t.add([c[i]]);
24445         }
24446         var el = document.createElement("div");
24447         el.className = this.itemCls;
24448         t.overwrite(el);
24449         container.dom.insertBefore(el, position);
24450         this.el = Roo.get(el);
24451         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24452         if(this.clickEvent != 'click'){
24453             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24454         }
24455     },
24456
24457     // private
24458     afterRender : function(){
24459         Roo.ColorPalette.superclass.afterRender.call(this);
24460         if(this.value){
24461             var s = this.value;
24462             this.value = null;
24463             this.select(s);
24464         }
24465     },
24466
24467     // private
24468     handleClick : function(e, t){
24469         e.preventDefault();
24470         if(!this.disabled){
24471             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24472             this.select(c.toUpperCase());
24473         }
24474     },
24475
24476     /**
24477      * Selects the specified color in the palette (fires the select event)
24478      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24479      */
24480     select : function(color){
24481         color = color.replace("#", "");
24482         if(color != this.value || this.allowReselect){
24483             var el = this.el;
24484             if(this.value){
24485                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24486             }
24487             el.child("a.color-"+color).addClass("x-color-palette-sel");
24488             this.value = color;
24489             this.fireEvent("select", this, color);
24490         }
24491     }
24492 });/*
24493  * Based on:
24494  * Ext JS Library 1.1.1
24495  * Copyright(c) 2006-2007, Ext JS, LLC.
24496  *
24497  * Originally Released Under LGPL - original licence link has changed is not relivant.
24498  *
24499  * Fork - LGPL
24500  * <script type="text/javascript">
24501  */
24502  
24503 /**
24504  * @class Roo.DatePicker
24505  * @extends Roo.Component
24506  * Simple date picker class.
24507  * @constructor
24508  * Create a new DatePicker
24509  * @param {Object} config The config object
24510  */
24511 Roo.DatePicker = function(config){
24512     Roo.DatePicker.superclass.constructor.call(this, config);
24513
24514     this.value = config && config.value ?
24515                  config.value.clearTime() : new Date().clearTime();
24516
24517     this.addEvents({
24518         /**
24519              * @event select
24520              * Fires when a date is selected
24521              * @param {DatePicker} this
24522              * @param {Date} date The selected date
24523              */
24524         select: true
24525     });
24526
24527     if(this.handler){
24528         this.on("select", this.handler,  this.scope || this);
24529     }
24530     // build the disabledDatesRE
24531     if(!this.disabledDatesRE && this.disabledDates){
24532         var dd = this.disabledDates;
24533         var re = "(?:";
24534         for(var i = 0; i < dd.length; i++){
24535             re += dd[i];
24536             if(i != dd.length-1) re += "|";
24537         }
24538         this.disabledDatesRE = new RegExp(re + ")");
24539     }
24540 };
24541
24542 Roo.extend(Roo.DatePicker, Roo.Component, {
24543     /**
24544      * @cfg {String} todayText
24545      * The text to display on the button that selects the current date (defaults to "Today")
24546      */
24547     todayText : "Today",
24548     /**
24549      * @cfg {String} okText
24550      * The text to display on the ok button
24551      */
24552     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24553     /**
24554      * @cfg {String} cancelText
24555      * The text to display on the cancel button
24556      */
24557     cancelText : "Cancel",
24558     /**
24559      * @cfg {String} todayTip
24560      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24561      */
24562     todayTip : "{0} (Spacebar)",
24563     /**
24564      * @cfg {Date} minDate
24565      * Minimum allowable date (JavaScript date object, defaults to null)
24566      */
24567     minDate : null,
24568     /**
24569      * @cfg {Date} maxDate
24570      * Maximum allowable date (JavaScript date object, defaults to null)
24571      */
24572     maxDate : null,
24573     /**
24574      * @cfg {String} minText
24575      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24576      */
24577     minText : "This date is before the minimum date",
24578     /**
24579      * @cfg {String} maxText
24580      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24581      */
24582     maxText : "This date is after the maximum date",
24583     /**
24584      * @cfg {String} format
24585      * The default date format string which can be overriden for localization support.  The format must be
24586      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24587      */
24588     format : "m/d/y",
24589     /**
24590      * @cfg {Array} disabledDays
24591      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24592      */
24593     disabledDays : null,
24594     /**
24595      * @cfg {String} disabledDaysText
24596      * The tooltip to display when the date falls on a disabled day (defaults to "")
24597      */
24598     disabledDaysText : "",
24599     /**
24600      * @cfg {RegExp} disabledDatesRE
24601      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24602      */
24603     disabledDatesRE : null,
24604     /**
24605      * @cfg {String} disabledDatesText
24606      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24607      */
24608     disabledDatesText : "",
24609     /**
24610      * @cfg {Boolean} constrainToViewport
24611      * True to constrain the date picker to the viewport (defaults to true)
24612      */
24613     constrainToViewport : true,
24614     /**
24615      * @cfg {Array} monthNames
24616      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24617      */
24618     monthNames : Date.monthNames,
24619     /**
24620      * @cfg {Array} dayNames
24621      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24622      */
24623     dayNames : Date.dayNames,
24624     /**
24625      * @cfg {String} nextText
24626      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24627      */
24628     nextText: 'Next Month (Control+Right)',
24629     /**
24630      * @cfg {String} prevText
24631      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24632      */
24633     prevText: 'Previous Month (Control+Left)',
24634     /**
24635      * @cfg {String} monthYearText
24636      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24637      */
24638     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24639     /**
24640      * @cfg {Number} startDay
24641      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24642      */
24643     startDay : 0,
24644     /**
24645      * @cfg {Bool} showClear
24646      * Show a clear button (usefull for date form elements that can be blank.)
24647      */
24648     
24649     showClear: false,
24650     
24651     /**
24652      * Sets the value of the date field
24653      * @param {Date} value The date to set
24654      */
24655     setValue : function(value){
24656         var old = this.value;
24657         this.value = value.clearTime(true);
24658         if(this.el){
24659             this.update(this.value);
24660         }
24661     },
24662
24663     /**
24664      * Gets the current selected value of the date field
24665      * @return {Date} The selected date
24666      */
24667     getValue : function(){
24668         return this.value;
24669     },
24670
24671     // private
24672     focus : function(){
24673         if(this.el){
24674             this.update(this.activeDate);
24675         }
24676     },
24677
24678     // private
24679     onRender : function(container, position){
24680         var m = [
24681              '<table cellspacing="0">',
24682                 '<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>',
24683                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24684         var dn = this.dayNames;
24685         for(var i = 0; i < 7; i++){
24686             var d = this.startDay+i;
24687             if(d > 6){
24688                 d = d-7;
24689             }
24690             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24691         }
24692         m[m.length] = "</tr></thead><tbody><tr>";
24693         for(var i = 0; i < 42; i++) {
24694             if(i % 7 == 0 && i != 0){
24695                 m[m.length] = "</tr><tr>";
24696             }
24697             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24698         }
24699         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24700             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24701
24702         var el = document.createElement("div");
24703         el.className = "x-date-picker";
24704         el.innerHTML = m.join("");
24705
24706         container.dom.insertBefore(el, position);
24707
24708         this.el = Roo.get(el);
24709         this.eventEl = Roo.get(el.firstChild);
24710
24711         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24712             handler: this.showPrevMonth,
24713             scope: this,
24714             preventDefault:true,
24715             stopDefault:true
24716         });
24717
24718         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24719             handler: this.showNextMonth,
24720             scope: this,
24721             preventDefault:true,
24722             stopDefault:true
24723         });
24724
24725         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24726
24727         this.monthPicker = this.el.down('div.x-date-mp');
24728         this.monthPicker.enableDisplayMode('block');
24729         
24730         var kn = new Roo.KeyNav(this.eventEl, {
24731             "left" : function(e){
24732                 e.ctrlKey ?
24733                     this.showPrevMonth() :
24734                     this.update(this.activeDate.add("d", -1));
24735             },
24736
24737             "right" : function(e){
24738                 e.ctrlKey ?
24739                     this.showNextMonth() :
24740                     this.update(this.activeDate.add("d", 1));
24741             },
24742
24743             "up" : function(e){
24744                 e.ctrlKey ?
24745                     this.showNextYear() :
24746                     this.update(this.activeDate.add("d", -7));
24747             },
24748
24749             "down" : function(e){
24750                 e.ctrlKey ?
24751                     this.showPrevYear() :
24752                     this.update(this.activeDate.add("d", 7));
24753             },
24754
24755             "pageUp" : function(e){
24756                 this.showNextMonth();
24757             },
24758
24759             "pageDown" : function(e){
24760                 this.showPrevMonth();
24761             },
24762
24763             "enter" : function(e){
24764                 e.stopPropagation();
24765                 return true;
24766             },
24767
24768             scope : this
24769         });
24770
24771         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24772
24773         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24774
24775         this.el.unselectable();
24776         
24777         this.cells = this.el.select("table.x-date-inner tbody td");
24778         this.textNodes = this.el.query("table.x-date-inner tbody span");
24779
24780         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24781             text: "&#160;",
24782             tooltip: this.monthYearText
24783         });
24784
24785         this.mbtn.on('click', this.showMonthPicker, this);
24786         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24787
24788
24789         var today = (new Date()).dateFormat(this.format);
24790         
24791         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24792         if (this.showClear) {
24793             baseTb.add( new Roo.Toolbar.Fill());
24794         }
24795         baseTb.add({
24796             text: String.format(this.todayText, today),
24797             tooltip: String.format(this.todayTip, today),
24798             handler: this.selectToday,
24799             scope: this
24800         });
24801         
24802         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24803             
24804         //});
24805         if (this.showClear) {
24806             
24807             baseTb.add( new Roo.Toolbar.Fill());
24808             baseTb.add({
24809                 text: '&#160;',
24810                 cls: 'x-btn-icon x-btn-clear',
24811                 handler: function() {
24812                     //this.value = '';
24813                     this.fireEvent("select", this, '');
24814                 },
24815                 scope: this
24816             });
24817         }
24818         
24819         
24820         if(Roo.isIE){
24821             this.el.repaint();
24822         }
24823         this.update(this.value);
24824     },
24825
24826     createMonthPicker : function(){
24827         if(!this.monthPicker.dom.firstChild){
24828             var buf = ['<table border="0" cellspacing="0">'];
24829             for(var i = 0; i < 6; i++){
24830                 buf.push(
24831                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24832                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24833                     i == 0 ?
24834                     '<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>' :
24835                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24836                 );
24837             }
24838             buf.push(
24839                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24840                     this.okText,
24841                     '</button><button type="button" class="x-date-mp-cancel">',
24842                     this.cancelText,
24843                     '</button></td></tr>',
24844                 '</table>'
24845             );
24846             this.monthPicker.update(buf.join(''));
24847             this.monthPicker.on('click', this.onMonthClick, this);
24848             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24849
24850             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24851             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24852
24853             this.mpMonths.each(function(m, a, i){
24854                 i += 1;
24855                 if((i%2) == 0){
24856                     m.dom.xmonth = 5 + Math.round(i * .5);
24857                 }else{
24858                     m.dom.xmonth = Math.round((i-1) * .5);
24859                 }
24860             });
24861         }
24862     },
24863
24864     showMonthPicker : function(){
24865         this.createMonthPicker();
24866         var size = this.el.getSize();
24867         this.monthPicker.setSize(size);
24868         this.monthPicker.child('table').setSize(size);
24869
24870         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24871         this.updateMPMonth(this.mpSelMonth);
24872         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24873         this.updateMPYear(this.mpSelYear);
24874
24875         this.monthPicker.slideIn('t', {duration:.2});
24876     },
24877
24878     updateMPYear : function(y){
24879         this.mpyear = y;
24880         var ys = this.mpYears.elements;
24881         for(var i = 1; i <= 10; i++){
24882             var td = ys[i-1], y2;
24883             if((i%2) == 0){
24884                 y2 = y + Math.round(i * .5);
24885                 td.firstChild.innerHTML = y2;
24886                 td.xyear = y2;
24887             }else{
24888                 y2 = y - (5-Math.round(i * .5));
24889                 td.firstChild.innerHTML = y2;
24890                 td.xyear = y2;
24891             }
24892             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24893         }
24894     },
24895
24896     updateMPMonth : function(sm){
24897         this.mpMonths.each(function(m, a, i){
24898             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24899         });
24900     },
24901
24902     selectMPMonth: function(m){
24903         
24904     },
24905
24906     onMonthClick : function(e, t){
24907         e.stopEvent();
24908         var el = new Roo.Element(t), pn;
24909         if(el.is('button.x-date-mp-cancel')){
24910             this.hideMonthPicker();
24911         }
24912         else if(el.is('button.x-date-mp-ok')){
24913             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24914             this.hideMonthPicker();
24915         }
24916         else if(pn = el.up('td.x-date-mp-month', 2)){
24917             this.mpMonths.removeClass('x-date-mp-sel');
24918             pn.addClass('x-date-mp-sel');
24919             this.mpSelMonth = pn.dom.xmonth;
24920         }
24921         else if(pn = el.up('td.x-date-mp-year', 2)){
24922             this.mpYears.removeClass('x-date-mp-sel');
24923             pn.addClass('x-date-mp-sel');
24924             this.mpSelYear = pn.dom.xyear;
24925         }
24926         else if(el.is('a.x-date-mp-prev')){
24927             this.updateMPYear(this.mpyear-10);
24928         }
24929         else if(el.is('a.x-date-mp-next')){
24930             this.updateMPYear(this.mpyear+10);
24931         }
24932     },
24933
24934     onMonthDblClick : function(e, t){
24935         e.stopEvent();
24936         var el = new Roo.Element(t), pn;
24937         if(pn = el.up('td.x-date-mp-month', 2)){
24938             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24939             this.hideMonthPicker();
24940         }
24941         else if(pn = el.up('td.x-date-mp-year', 2)){
24942             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24943             this.hideMonthPicker();
24944         }
24945     },
24946
24947     hideMonthPicker : function(disableAnim){
24948         if(this.monthPicker){
24949             if(disableAnim === true){
24950                 this.monthPicker.hide();
24951             }else{
24952                 this.monthPicker.slideOut('t', {duration:.2});
24953             }
24954         }
24955     },
24956
24957     // private
24958     showPrevMonth : function(e){
24959         this.update(this.activeDate.add("mo", -1));
24960     },
24961
24962     // private
24963     showNextMonth : function(e){
24964         this.update(this.activeDate.add("mo", 1));
24965     },
24966
24967     // private
24968     showPrevYear : function(){
24969         this.update(this.activeDate.add("y", -1));
24970     },
24971
24972     // private
24973     showNextYear : function(){
24974         this.update(this.activeDate.add("y", 1));
24975     },
24976
24977     // private
24978     handleMouseWheel : function(e){
24979         var delta = e.getWheelDelta();
24980         if(delta > 0){
24981             this.showPrevMonth();
24982             e.stopEvent();
24983         } else if(delta < 0){
24984             this.showNextMonth();
24985             e.stopEvent();
24986         }
24987     },
24988
24989     // private
24990     handleDateClick : function(e, t){
24991         e.stopEvent();
24992         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24993             this.setValue(new Date(t.dateValue));
24994             this.fireEvent("select", this, this.value);
24995         }
24996     },
24997
24998     // private
24999     selectToday : function(){
25000         this.setValue(new Date().clearTime());
25001         this.fireEvent("select", this, this.value);
25002     },
25003
25004     // private
25005     update : function(date){
25006         var vd = this.activeDate;
25007         this.activeDate = date;
25008         if(vd && this.el){
25009             var t = date.getTime();
25010             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25011                 this.cells.removeClass("x-date-selected");
25012                 this.cells.each(function(c){
25013                    if(c.dom.firstChild.dateValue == t){
25014                        c.addClass("x-date-selected");
25015                        setTimeout(function(){
25016                             try{c.dom.firstChild.focus();}catch(e){}
25017                        }, 50);
25018                        return false;
25019                    }
25020                 });
25021                 return;
25022             }
25023         }
25024         var days = date.getDaysInMonth();
25025         var firstOfMonth = date.getFirstDateOfMonth();
25026         var startingPos = firstOfMonth.getDay()-this.startDay;
25027
25028         if(startingPos <= this.startDay){
25029             startingPos += 7;
25030         }
25031
25032         var pm = date.add("mo", -1);
25033         var prevStart = pm.getDaysInMonth()-startingPos;
25034
25035         var cells = this.cells.elements;
25036         var textEls = this.textNodes;
25037         days += startingPos;
25038
25039         // convert everything to numbers so it's fast
25040         var day = 86400000;
25041         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25042         var today = new Date().clearTime().getTime();
25043         var sel = date.clearTime().getTime();
25044         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25045         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25046         var ddMatch = this.disabledDatesRE;
25047         var ddText = this.disabledDatesText;
25048         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25049         var ddaysText = this.disabledDaysText;
25050         var format = this.format;
25051
25052         var setCellClass = function(cal, cell){
25053             cell.title = "";
25054             var t = d.getTime();
25055             cell.firstChild.dateValue = t;
25056             if(t == today){
25057                 cell.className += " x-date-today";
25058                 cell.title = cal.todayText;
25059             }
25060             if(t == sel){
25061                 cell.className += " x-date-selected";
25062                 setTimeout(function(){
25063                     try{cell.firstChild.focus();}catch(e){}
25064                 }, 50);
25065             }
25066             // disabling
25067             if(t < min) {
25068                 cell.className = " x-date-disabled";
25069                 cell.title = cal.minText;
25070                 return;
25071             }
25072             if(t > max) {
25073                 cell.className = " x-date-disabled";
25074                 cell.title = cal.maxText;
25075                 return;
25076             }
25077             if(ddays){
25078                 if(ddays.indexOf(d.getDay()) != -1){
25079                     cell.title = ddaysText;
25080                     cell.className = " x-date-disabled";
25081                 }
25082             }
25083             if(ddMatch && format){
25084                 var fvalue = d.dateFormat(format);
25085                 if(ddMatch.test(fvalue)){
25086                     cell.title = ddText.replace("%0", fvalue);
25087                     cell.className = " x-date-disabled";
25088                 }
25089             }
25090         };
25091
25092         var i = 0;
25093         for(; i < startingPos; i++) {
25094             textEls[i].innerHTML = (++prevStart);
25095             d.setDate(d.getDate()+1);
25096             cells[i].className = "x-date-prevday";
25097             setCellClass(this, cells[i]);
25098         }
25099         for(; i < days; i++){
25100             intDay = i - startingPos + 1;
25101             textEls[i].innerHTML = (intDay);
25102             d.setDate(d.getDate()+1);
25103             cells[i].className = "x-date-active";
25104             setCellClass(this, cells[i]);
25105         }
25106         var extraDays = 0;
25107         for(; i < 42; i++) {
25108              textEls[i].innerHTML = (++extraDays);
25109              d.setDate(d.getDate()+1);
25110              cells[i].className = "x-date-nextday";
25111              setCellClass(this, cells[i]);
25112         }
25113
25114         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25115
25116         if(!this.internalRender){
25117             var main = this.el.dom.firstChild;
25118             var w = main.offsetWidth;
25119             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25120             Roo.fly(main).setWidth(w);
25121             this.internalRender = true;
25122             // opera does not respect the auto grow header center column
25123             // then, after it gets a width opera refuses to recalculate
25124             // without a second pass
25125             if(Roo.isOpera && !this.secondPass){
25126                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25127                 this.secondPass = true;
25128                 this.update.defer(10, this, [date]);
25129             }
25130         }
25131     }
25132 });/*
25133  * Based on:
25134  * Ext JS Library 1.1.1
25135  * Copyright(c) 2006-2007, Ext JS, LLC.
25136  *
25137  * Originally Released Under LGPL - original licence link has changed is not relivant.
25138  *
25139  * Fork - LGPL
25140  * <script type="text/javascript">
25141  */
25142 /**
25143  * @class Roo.TabPanel
25144  * @extends Roo.util.Observable
25145  * A lightweight tab container.
25146  * <br><br>
25147  * Usage:
25148  * <pre><code>
25149 // basic tabs 1, built from existing content
25150 var tabs = new Roo.TabPanel("tabs1");
25151 tabs.addTab("script", "View Script");
25152 tabs.addTab("markup", "View Markup");
25153 tabs.activate("script");
25154
25155 // more advanced tabs, built from javascript
25156 var jtabs = new Roo.TabPanel("jtabs");
25157 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25158
25159 // set up the UpdateManager
25160 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25161 var updater = tab2.getUpdateManager();
25162 updater.setDefaultUrl("ajax1.htm");
25163 tab2.on('activate', updater.refresh, updater, true);
25164
25165 // Use setUrl for Ajax loading
25166 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25167 tab3.setUrl("ajax2.htm", null, true);
25168
25169 // Disabled tab
25170 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25171 tab4.disable();
25172
25173 jtabs.activate("jtabs-1");
25174  * </code></pre>
25175  * @constructor
25176  * Create a new TabPanel.
25177  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25178  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25179  */
25180 Roo.TabPanel = function(container, config){
25181     /**
25182     * The container element for this TabPanel.
25183     * @type Roo.Element
25184     */
25185     this.el = Roo.get(container, true);
25186     if(config){
25187         if(typeof config == "boolean"){
25188             this.tabPosition = config ? "bottom" : "top";
25189         }else{
25190             Roo.apply(this, config);
25191         }
25192     }
25193     if(this.tabPosition == "bottom"){
25194         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25195         this.el.addClass("x-tabs-bottom");
25196     }
25197     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25198     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25199     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25200     if(Roo.isIE){
25201         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25202     }
25203     if(this.tabPosition != "bottom"){
25204     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25205      * @type Roo.Element
25206      */
25207       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25208       this.el.addClass("x-tabs-top");
25209     }
25210     this.items = [];
25211
25212     this.bodyEl.setStyle("position", "relative");
25213
25214     this.active = null;
25215     this.activateDelegate = this.activate.createDelegate(this);
25216
25217     this.addEvents({
25218         /**
25219          * @event tabchange
25220          * Fires when the active tab changes
25221          * @param {Roo.TabPanel} this
25222          * @param {Roo.TabPanelItem} activePanel The new active tab
25223          */
25224         "tabchange": true,
25225         /**
25226          * @event beforetabchange
25227          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25228          * @param {Roo.TabPanel} this
25229          * @param {Object} e Set cancel to true on this object to cancel the tab change
25230          * @param {Roo.TabPanelItem} tab The tab being changed to
25231          */
25232         "beforetabchange" : true
25233     });
25234
25235     Roo.EventManager.onWindowResize(this.onResize, this);
25236     this.cpad = this.el.getPadding("lr");
25237     this.hiddenCount = 0;
25238
25239     Roo.TabPanel.superclass.constructor.call(this);
25240 };
25241
25242 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25243         /*
25244          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25245          */
25246     tabPosition : "top",
25247         /*
25248          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25249          */
25250     currentTabWidth : 0,
25251         /*
25252          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25253          */
25254     minTabWidth : 40,
25255         /*
25256          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25257          */
25258     maxTabWidth : 250,
25259         /*
25260          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25261          */
25262     preferredTabWidth : 175,
25263         /*
25264          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25265          */
25266     resizeTabs : false,
25267         /*
25268          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25269          */
25270     monitorResize : true,
25271
25272     /**
25273      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25274      * @param {String} id The id of the div to use <b>or create</b>
25275      * @param {String} text The text for the tab
25276      * @param {String} content (optional) Content to put in the TabPanelItem body
25277      * @param {Boolean} closable (optional) True to create a close icon on the tab
25278      * @return {Roo.TabPanelItem} The created TabPanelItem
25279      */
25280     addTab : function(id, text, content, closable){
25281         var item = new Roo.TabPanelItem(this, id, text, closable);
25282         this.addTabItem(item);
25283         if(content){
25284             item.setContent(content);
25285         }
25286         return item;
25287     },
25288
25289     /**
25290      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25291      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25292      * @return {Roo.TabPanelItem}
25293      */
25294     getTab : function(id){
25295         return this.items[id];
25296     },
25297
25298     /**
25299      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25300      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25301      */
25302     hideTab : function(id){
25303         var t = this.items[id];
25304         if(!t.isHidden()){
25305            t.setHidden(true);
25306            this.hiddenCount++;
25307            this.autoSizeTabs();
25308         }
25309     },
25310
25311     /**
25312      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25313      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25314      */
25315     unhideTab : function(id){
25316         var t = this.items[id];
25317         if(t.isHidden()){
25318            t.setHidden(false);
25319            this.hiddenCount--;
25320            this.autoSizeTabs();
25321         }
25322     },
25323
25324     /**
25325      * Adds an existing {@link Roo.TabPanelItem}.
25326      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25327      */
25328     addTabItem : function(item){
25329         this.items[item.id] = item;
25330         this.items.push(item);
25331         if(this.resizeTabs){
25332            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25333            this.autoSizeTabs();
25334         }else{
25335             item.autoSize();
25336         }
25337     },
25338
25339     /**
25340      * Removes a {@link Roo.TabPanelItem}.
25341      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25342      */
25343     removeTab : function(id){
25344         var items = this.items;
25345         var tab = items[id];
25346         if(!tab) { return; }
25347         var index = items.indexOf(tab);
25348         if(this.active == tab && items.length > 1){
25349             var newTab = this.getNextAvailable(index);
25350             if(newTab) {
25351                 newTab.activate();
25352             }
25353         }
25354         this.stripEl.dom.removeChild(tab.pnode.dom);
25355         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25356             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25357         }
25358         items.splice(index, 1);
25359         delete this.items[tab.id];
25360         tab.fireEvent("close", tab);
25361         tab.purgeListeners();
25362         this.autoSizeTabs();
25363     },
25364
25365     getNextAvailable : function(start){
25366         var items = this.items;
25367         var index = start;
25368         // look for a next tab that will slide over to
25369         // replace the one being removed
25370         while(index < items.length){
25371             var item = items[++index];
25372             if(item && !item.isHidden()){
25373                 return item;
25374             }
25375         }
25376         // if one isn't found select the previous tab (on the left)
25377         index = start;
25378         while(index >= 0){
25379             var item = items[--index];
25380             if(item && !item.isHidden()){
25381                 return item;
25382             }
25383         }
25384         return null;
25385     },
25386
25387     /**
25388      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25389      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25390      */
25391     disableTab : function(id){
25392         var tab = this.items[id];
25393         if(tab && this.active != tab){
25394             tab.disable();
25395         }
25396     },
25397
25398     /**
25399      * Enables a {@link Roo.TabPanelItem} that is disabled.
25400      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25401      */
25402     enableTab : function(id){
25403         var tab = this.items[id];
25404         tab.enable();
25405     },
25406
25407     /**
25408      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25409      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25410      * @return {Roo.TabPanelItem} The TabPanelItem.
25411      */
25412     activate : function(id){
25413         var tab = this.items[id];
25414         if(!tab){
25415             return null;
25416         }
25417         if(tab == this.active || tab.disabled){
25418             return tab;
25419         }
25420         var e = {};
25421         this.fireEvent("beforetabchange", this, e, tab);
25422         if(e.cancel !== true && !tab.disabled){
25423             if(this.active){
25424                 this.active.hide();
25425             }
25426             this.active = this.items[id];
25427             this.active.show();
25428             this.fireEvent("tabchange", this, this.active);
25429         }
25430         return tab;
25431     },
25432
25433     /**
25434      * Gets the active {@link Roo.TabPanelItem}.
25435      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25436      */
25437     getActiveTab : function(){
25438         return this.active;
25439     },
25440
25441     /**
25442      * Updates the tab body element to fit the height of the container element
25443      * for overflow scrolling
25444      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25445      */
25446     syncHeight : function(targetHeight){
25447         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25448         var bm = this.bodyEl.getMargins();
25449         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25450         this.bodyEl.setHeight(newHeight);
25451         return newHeight;
25452     },
25453
25454     onResize : function(){
25455         if(this.monitorResize){
25456             this.autoSizeTabs();
25457         }
25458     },
25459
25460     /**
25461      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25462      */
25463     beginUpdate : function(){
25464         this.updating = true;
25465     },
25466
25467     /**
25468      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25469      */
25470     endUpdate : function(){
25471         this.updating = false;
25472         this.autoSizeTabs();
25473     },
25474
25475     /**
25476      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25477      */
25478     autoSizeTabs : function(){
25479         var count = this.items.length;
25480         var vcount = count - this.hiddenCount;
25481         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25482         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25483         var availWidth = Math.floor(w / vcount);
25484         var b = this.stripBody;
25485         if(b.getWidth() > w){
25486             var tabs = this.items;
25487             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25488             if(availWidth < this.minTabWidth){
25489                 /*if(!this.sleft){    // incomplete scrolling code
25490                     this.createScrollButtons();
25491                 }
25492                 this.showScroll();
25493                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25494             }
25495         }else{
25496             if(this.currentTabWidth < this.preferredTabWidth){
25497                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25498             }
25499         }
25500     },
25501
25502     /**
25503      * Returns the number of tabs in this TabPanel.
25504      * @return {Number}
25505      */
25506      getCount : function(){
25507          return this.items.length;
25508      },
25509
25510     /**
25511      * Resizes all the tabs to the passed width
25512      * @param {Number} The new width
25513      */
25514     setTabWidth : function(width){
25515         this.currentTabWidth = width;
25516         for(var i = 0, len = this.items.length; i < len; i++) {
25517                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25518         }
25519     },
25520
25521     /**
25522      * Destroys this TabPanel
25523      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25524      */
25525     destroy : function(removeEl){
25526         Roo.EventManager.removeResizeListener(this.onResize, this);
25527         for(var i = 0, len = this.items.length; i < len; i++){
25528             this.items[i].purgeListeners();
25529         }
25530         if(removeEl === true){
25531             this.el.update("");
25532             this.el.remove();
25533         }
25534     }
25535 });
25536
25537 /**
25538  * @class Roo.TabPanelItem
25539  * @extends Roo.util.Observable
25540  * Represents an individual item (tab plus body) in a TabPanel.
25541  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25542  * @param {String} id The id of this TabPanelItem
25543  * @param {String} text The text for the tab of this TabPanelItem
25544  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25545  */
25546 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25547     /**
25548      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25549      * @type Roo.TabPanel
25550      */
25551     this.tabPanel = tabPanel;
25552     /**
25553      * The id for this TabPanelItem
25554      * @type String
25555      */
25556     this.id = id;
25557     /** @private */
25558     this.disabled = false;
25559     /** @private */
25560     this.text = text;
25561     /** @private */
25562     this.loaded = false;
25563     this.closable = closable;
25564
25565     /**
25566      * The body element for this TabPanelItem.
25567      * @type Roo.Element
25568      */
25569     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25570     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25571     this.bodyEl.setStyle("display", "block");
25572     this.bodyEl.setStyle("zoom", "1");
25573     this.hideAction();
25574
25575     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25576     /** @private */
25577     this.el = Roo.get(els.el, true);
25578     this.inner = Roo.get(els.inner, true);
25579     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25580     this.pnode = Roo.get(els.el.parentNode, true);
25581     this.el.on("mousedown", this.onTabMouseDown, this);
25582     this.el.on("click", this.onTabClick, this);
25583     /** @private */
25584     if(closable){
25585         var c = Roo.get(els.close, true);
25586         c.dom.title = this.closeText;
25587         c.addClassOnOver("close-over");
25588         c.on("click", this.closeClick, this);
25589      }
25590
25591     this.addEvents({
25592          /**
25593          * @event activate
25594          * Fires when this tab becomes the active tab.
25595          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25596          * @param {Roo.TabPanelItem} this
25597          */
25598         "activate": true,
25599         /**
25600          * @event beforeclose
25601          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25602          * @param {Roo.TabPanelItem} this
25603          * @param {Object} e Set cancel to true on this object to cancel the close.
25604          */
25605         "beforeclose": true,
25606         /**
25607          * @event close
25608          * Fires when this tab is closed.
25609          * @param {Roo.TabPanelItem} this
25610          */
25611          "close": true,
25612         /**
25613          * @event deactivate
25614          * Fires when this tab is no longer the active tab.
25615          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25616          * @param {Roo.TabPanelItem} this
25617          */
25618          "deactivate" : true
25619     });
25620     this.hidden = false;
25621
25622     Roo.TabPanelItem.superclass.constructor.call(this);
25623 };
25624
25625 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25626     purgeListeners : function(){
25627        Roo.util.Observable.prototype.purgeListeners.call(this);
25628        this.el.removeAllListeners();
25629     },
25630     /**
25631      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25632      */
25633     show : function(){
25634         this.pnode.addClass("on");
25635         this.showAction();
25636         if(Roo.isOpera){
25637             this.tabPanel.stripWrap.repaint();
25638         }
25639         this.fireEvent("activate", this.tabPanel, this);
25640     },
25641
25642     /**
25643      * Returns true if this tab is the active tab.
25644      * @return {Boolean}
25645      */
25646     isActive : function(){
25647         return this.tabPanel.getActiveTab() == this;
25648     },
25649
25650     /**
25651      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25652      */
25653     hide : function(){
25654         this.pnode.removeClass("on");
25655         this.hideAction();
25656         this.fireEvent("deactivate", this.tabPanel, this);
25657     },
25658
25659     hideAction : function(){
25660         this.bodyEl.hide();
25661         this.bodyEl.setStyle("position", "absolute");
25662         this.bodyEl.setLeft("-20000px");
25663         this.bodyEl.setTop("-20000px");
25664     },
25665
25666     showAction : function(){
25667         this.bodyEl.setStyle("position", "relative");
25668         this.bodyEl.setTop("");
25669         this.bodyEl.setLeft("");
25670         this.bodyEl.show();
25671     },
25672
25673     /**
25674      * Set the tooltip for the tab.
25675      * @param {String} tooltip The tab's tooltip
25676      */
25677     setTooltip : function(text){
25678         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25679             this.textEl.dom.qtip = text;
25680             this.textEl.dom.removeAttribute('title');
25681         }else{
25682             this.textEl.dom.title = text;
25683         }
25684     },
25685
25686     onTabClick : function(e){
25687         e.preventDefault();
25688         this.tabPanel.activate(this.id);
25689     },
25690
25691     onTabMouseDown : function(e){
25692         e.preventDefault();
25693         this.tabPanel.activate(this.id);
25694     },
25695
25696     getWidth : function(){
25697         return this.inner.getWidth();
25698     },
25699
25700     setWidth : function(width){
25701         var iwidth = width - this.pnode.getPadding("lr");
25702         this.inner.setWidth(iwidth);
25703         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25704         this.pnode.setWidth(width);
25705     },
25706
25707     /**
25708      * Show or hide the tab
25709      * @param {Boolean} hidden True to hide or false to show.
25710      */
25711     setHidden : function(hidden){
25712         this.hidden = hidden;
25713         this.pnode.setStyle("display", hidden ? "none" : "");
25714     },
25715
25716     /**
25717      * Returns true if this tab is "hidden"
25718      * @return {Boolean}
25719      */
25720     isHidden : function(){
25721         return this.hidden;
25722     },
25723
25724     /**
25725      * Returns the text for this tab
25726      * @return {String}
25727      */
25728     getText : function(){
25729         return this.text;
25730     },
25731
25732     autoSize : function(){
25733         //this.el.beginMeasure();
25734         this.textEl.setWidth(1);
25735         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25736         //this.el.endMeasure();
25737     },
25738
25739     /**
25740      * Sets the text for the tab (Note: this also sets the tooltip text)
25741      * @param {String} text The tab's text and tooltip
25742      */
25743     setText : function(text){
25744         this.text = text;
25745         this.textEl.update(text);
25746         this.setTooltip(text);
25747         if(!this.tabPanel.resizeTabs){
25748             this.autoSize();
25749         }
25750     },
25751     /**
25752      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25753      */
25754     activate : function(){
25755         this.tabPanel.activate(this.id);
25756     },
25757
25758     /**
25759      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25760      */
25761     disable : function(){
25762         if(this.tabPanel.active != this){
25763             this.disabled = true;
25764             this.pnode.addClass("disabled");
25765         }
25766     },
25767
25768     /**
25769      * Enables this TabPanelItem if it was previously disabled.
25770      */
25771     enable : function(){
25772         this.disabled = false;
25773         this.pnode.removeClass("disabled");
25774     },
25775
25776     /**
25777      * Sets the content for this TabPanelItem.
25778      * @param {String} content The content
25779      * @param {Boolean} loadScripts true to look for and load scripts
25780      */
25781     setContent : function(content, loadScripts){
25782         this.bodyEl.update(content, loadScripts);
25783     },
25784
25785     /**
25786      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25787      * @return {Roo.UpdateManager} The UpdateManager
25788      */
25789     getUpdateManager : function(){
25790         return this.bodyEl.getUpdateManager();
25791     },
25792
25793     /**
25794      * Set a URL to be used to load the content for this TabPanelItem.
25795      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25796      * @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)
25797      * @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)
25798      * @return {Roo.UpdateManager} The UpdateManager
25799      */
25800     setUrl : function(url, params, loadOnce){
25801         if(this.refreshDelegate){
25802             this.un('activate', this.refreshDelegate);
25803         }
25804         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25805         this.on("activate", this.refreshDelegate);
25806         return this.bodyEl.getUpdateManager();
25807     },
25808
25809     /** @private */
25810     _handleRefresh : function(url, params, loadOnce){
25811         if(!loadOnce || !this.loaded){
25812             var updater = this.bodyEl.getUpdateManager();
25813             updater.update(url, params, this._setLoaded.createDelegate(this));
25814         }
25815     },
25816
25817     /**
25818      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25819      *   Will fail silently if the setUrl method has not been called.
25820      *   This does not activate the panel, just updates its content.
25821      */
25822     refresh : function(){
25823         if(this.refreshDelegate){
25824            this.loaded = false;
25825            this.refreshDelegate();
25826         }
25827     },
25828
25829     /** @private */
25830     _setLoaded : function(){
25831         this.loaded = true;
25832     },
25833
25834     /** @private */
25835     closeClick : function(e){
25836         var o = {};
25837         e.stopEvent();
25838         this.fireEvent("beforeclose", this, o);
25839         if(o.cancel !== true){
25840             this.tabPanel.removeTab(this.id);
25841         }
25842     },
25843     /**
25844      * The text displayed in the tooltip for the close icon.
25845      * @type String
25846      */
25847     closeText : "Close this tab"
25848 });
25849
25850 /** @private */
25851 Roo.TabPanel.prototype.createStrip = function(container){
25852     var strip = document.createElement("div");
25853     strip.className = "x-tabs-wrap";
25854     container.appendChild(strip);
25855     return strip;
25856 };
25857 /** @private */
25858 Roo.TabPanel.prototype.createStripList = function(strip){
25859     // div wrapper for retard IE
25860     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>';
25861     return strip.firstChild.firstChild.firstChild.firstChild;
25862 };
25863 /** @private */
25864 Roo.TabPanel.prototype.createBody = function(container){
25865     var body = document.createElement("div");
25866     Roo.id(body, "tab-body");
25867     Roo.fly(body).addClass("x-tabs-body");
25868     container.appendChild(body);
25869     return body;
25870 };
25871 /** @private */
25872 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25873     var body = Roo.getDom(id);
25874     if(!body){
25875         body = document.createElement("div");
25876         body.id = id;
25877     }
25878     Roo.fly(body).addClass("x-tabs-item-body");
25879     bodyEl.insertBefore(body, bodyEl.firstChild);
25880     return body;
25881 };
25882 /** @private */
25883 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25884     var td = document.createElement("td");
25885     stripEl.appendChild(td);
25886     if(closable){
25887         td.className = "x-tabs-closable";
25888         if(!this.closeTpl){
25889             this.closeTpl = new Roo.Template(
25890                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25891                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25892                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25893             );
25894         }
25895         var el = this.closeTpl.overwrite(td, {"text": text});
25896         var close = el.getElementsByTagName("div")[0];
25897         var inner = el.getElementsByTagName("em")[0];
25898         return {"el": el, "close": close, "inner": inner};
25899     } else {
25900         if(!this.tabTpl){
25901             this.tabTpl = new Roo.Template(
25902                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25903                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25904             );
25905         }
25906         var el = this.tabTpl.overwrite(td, {"text": text});
25907         var inner = el.getElementsByTagName("em")[0];
25908         return {"el": el, "inner": inner};
25909     }
25910 };/*
25911  * Based on:
25912  * Ext JS Library 1.1.1
25913  * Copyright(c) 2006-2007, Ext JS, LLC.
25914  *
25915  * Originally Released Under LGPL - original licence link has changed is not relivant.
25916  *
25917  * Fork - LGPL
25918  * <script type="text/javascript">
25919  */
25920
25921 /**
25922  * @class Roo.Button
25923  * @extends Roo.util.Observable
25924  * Simple Button class
25925  * @cfg {String} text The button text
25926  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25927  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25928  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25929  * @cfg {Object} scope The scope of the handler
25930  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25931  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25932  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25933  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25934  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25935  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25936    applies if enableToggle = true)
25937  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25938  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25939   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25940  * @constructor
25941  * Create a new button
25942  * @param {Object} config The config object
25943  */
25944 Roo.Button = function(renderTo, config)
25945 {
25946     if (!config) {
25947         config = renderTo;
25948         renderTo = config.renderTo || false;
25949     }
25950     
25951     Roo.apply(this, config);
25952     this.addEvents({
25953         /**
25954              * @event click
25955              * Fires when this button is clicked
25956              * @param {Button} this
25957              * @param {EventObject} e The click event
25958              */
25959             "click" : true,
25960         /**
25961              * @event toggle
25962              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25963              * @param {Button} this
25964              * @param {Boolean} pressed
25965              */
25966             "toggle" : true,
25967         /**
25968              * @event mouseover
25969              * Fires when the mouse hovers over the button
25970              * @param {Button} this
25971              * @param {Event} e The event object
25972              */
25973         'mouseover' : true,
25974         /**
25975              * @event mouseout
25976              * Fires when the mouse exits the button
25977              * @param {Button} this
25978              * @param {Event} e The event object
25979              */
25980         'mouseout': true,
25981          /**
25982              * @event render
25983              * Fires when the button is rendered
25984              * @param {Button} this
25985              */
25986         'render': true
25987     });
25988     if(this.menu){
25989         this.menu = Roo.menu.MenuMgr.get(this.menu);
25990     }
25991     // register listeners first!!  - so render can be captured..
25992     Roo.util.Observable.call(this);
25993     if(renderTo){
25994         this.render(renderTo);
25995     }
25996     
25997   
25998 };
25999
26000 Roo.extend(Roo.Button, Roo.util.Observable, {
26001     /**
26002      * 
26003      */
26004     
26005     /**
26006      * Read-only. True if this button is hidden
26007      * @type Boolean
26008      */
26009     hidden : false,
26010     /**
26011      * Read-only. True if this button is disabled
26012      * @type Boolean
26013      */
26014     disabled : false,
26015     /**
26016      * Read-only. True if this button is pressed (only if enableToggle = true)
26017      * @type Boolean
26018      */
26019     pressed : false,
26020
26021     /**
26022      * @cfg {Number} tabIndex 
26023      * The DOM tabIndex for this button (defaults to undefined)
26024      */
26025     tabIndex : undefined,
26026
26027     /**
26028      * @cfg {Boolean} enableToggle
26029      * True to enable pressed/not pressed toggling (defaults to false)
26030      */
26031     enableToggle: false,
26032     /**
26033      * @cfg {Mixed} menu
26034      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26035      */
26036     menu : undefined,
26037     /**
26038      * @cfg {String} menuAlign
26039      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26040      */
26041     menuAlign : "tl-bl?",
26042
26043     /**
26044      * @cfg {String} iconCls
26045      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26046      */
26047     iconCls : undefined,
26048     /**
26049      * @cfg {String} type
26050      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26051      */
26052     type : 'button',
26053
26054     // private
26055     menuClassTarget: 'tr',
26056
26057     /**
26058      * @cfg {String} clickEvent
26059      * The type of event to map to the button's event handler (defaults to 'click')
26060      */
26061     clickEvent : 'click',
26062
26063     /**
26064      * @cfg {Boolean} handleMouseEvents
26065      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26066      */
26067     handleMouseEvents : true,
26068
26069     /**
26070      * @cfg {String} tooltipType
26071      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26072      */
26073     tooltipType : 'qtip',
26074
26075     /**
26076      * @cfg {String} cls
26077      * A CSS class to apply to the button's main element.
26078      */
26079     
26080     /**
26081      * @cfg {Roo.Template} template (Optional)
26082      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26083      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26084      * require code modifications if required elements (e.g. a button) aren't present.
26085      */
26086
26087     // private
26088     render : function(renderTo){
26089         var btn;
26090         if(this.hideParent){
26091             this.parentEl = Roo.get(renderTo);
26092         }
26093         if(!this.dhconfig){
26094             if(!this.template){
26095                 if(!Roo.Button.buttonTemplate){
26096                     // hideous table template
26097                     Roo.Button.buttonTemplate = new Roo.Template(
26098                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26099                         '<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>',
26100                         "</tr></tbody></table>");
26101                 }
26102                 this.template = Roo.Button.buttonTemplate;
26103             }
26104             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26105             var btnEl = btn.child("button:first");
26106             btnEl.on('focus', this.onFocus, this);
26107             btnEl.on('blur', this.onBlur, this);
26108             if(this.cls){
26109                 btn.addClass(this.cls);
26110             }
26111             if(this.icon){
26112                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26113             }
26114             if(this.iconCls){
26115                 btnEl.addClass(this.iconCls);
26116                 if(!this.cls){
26117                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26118                 }
26119             }
26120             if(this.tabIndex !== undefined){
26121                 btnEl.dom.tabIndex = this.tabIndex;
26122             }
26123             if(this.tooltip){
26124                 if(typeof this.tooltip == 'object'){
26125                     Roo.QuickTips.tips(Roo.apply({
26126                           target: btnEl.id
26127                     }, this.tooltip));
26128                 } else {
26129                     btnEl.dom[this.tooltipType] = this.tooltip;
26130                 }
26131             }
26132         }else{
26133             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26134         }
26135         this.el = btn;
26136         if(this.id){
26137             this.el.dom.id = this.el.id = this.id;
26138         }
26139         if(this.menu){
26140             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26141             this.menu.on("show", this.onMenuShow, this);
26142             this.menu.on("hide", this.onMenuHide, this);
26143         }
26144         btn.addClass("x-btn");
26145         if(Roo.isIE && !Roo.isIE7){
26146             this.autoWidth.defer(1, this);
26147         }else{
26148             this.autoWidth();
26149         }
26150         if(this.handleMouseEvents){
26151             btn.on("mouseover", this.onMouseOver, this);
26152             btn.on("mouseout", this.onMouseOut, this);
26153             btn.on("mousedown", this.onMouseDown, this);
26154         }
26155         btn.on(this.clickEvent, this.onClick, this);
26156         //btn.on("mouseup", this.onMouseUp, this);
26157         if(this.hidden){
26158             this.hide();
26159         }
26160         if(this.disabled){
26161             this.disable();
26162         }
26163         Roo.ButtonToggleMgr.register(this);
26164         if(this.pressed){
26165             this.el.addClass("x-btn-pressed");
26166         }
26167         if(this.repeat){
26168             var repeater = new Roo.util.ClickRepeater(btn,
26169                 typeof this.repeat == "object" ? this.repeat : {}
26170             );
26171             repeater.on("click", this.onClick,  this);
26172         }
26173         
26174         this.fireEvent('render', this);
26175         
26176     },
26177     /**
26178      * Returns the button's underlying element
26179      * @return {Roo.Element} The element
26180      */
26181     getEl : function(){
26182         return this.el;  
26183     },
26184     
26185     /**
26186      * Destroys this Button and removes any listeners.
26187      */
26188     destroy : function(){
26189         Roo.ButtonToggleMgr.unregister(this);
26190         this.el.removeAllListeners();
26191         this.purgeListeners();
26192         this.el.remove();
26193     },
26194
26195     // private
26196     autoWidth : function(){
26197         if(this.el){
26198             this.el.setWidth("auto");
26199             if(Roo.isIE7 && Roo.isStrict){
26200                 var ib = this.el.child('button');
26201                 if(ib && ib.getWidth() > 20){
26202                     ib.clip();
26203                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26204                 }
26205             }
26206             if(this.minWidth){
26207                 if(this.hidden){
26208                     this.el.beginMeasure();
26209                 }
26210                 if(this.el.getWidth() < this.minWidth){
26211                     this.el.setWidth(this.minWidth);
26212                 }
26213                 if(this.hidden){
26214                     this.el.endMeasure();
26215                 }
26216             }
26217         }
26218     },
26219
26220     /**
26221      * Assigns this button's click handler
26222      * @param {Function} handler The function to call when the button is clicked
26223      * @param {Object} scope (optional) Scope for the function passed in
26224      */
26225     setHandler : function(handler, scope){
26226         this.handler = handler;
26227         this.scope = scope;  
26228     },
26229     
26230     /**
26231      * Sets this button's text
26232      * @param {String} text The button text
26233      */
26234     setText : function(text){
26235         this.text = text;
26236         if(this.el){
26237             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26238         }
26239         this.autoWidth();
26240     },
26241     
26242     /**
26243      * Gets the text for this button
26244      * @return {String} The button text
26245      */
26246     getText : function(){
26247         return this.text;  
26248     },
26249     
26250     /**
26251      * Show this button
26252      */
26253     show: function(){
26254         this.hidden = false;
26255         if(this.el){
26256             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26257         }
26258     },
26259     
26260     /**
26261      * Hide this button
26262      */
26263     hide: function(){
26264         this.hidden = true;
26265         if(this.el){
26266             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26267         }
26268     },
26269     
26270     /**
26271      * Convenience function for boolean show/hide
26272      * @param {Boolean} visible True to show, false to hide
26273      */
26274     setVisible: function(visible){
26275         if(visible) {
26276             this.show();
26277         }else{
26278             this.hide();
26279         }
26280     },
26281     
26282     /**
26283      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26284      * @param {Boolean} state (optional) Force a particular state
26285      */
26286     toggle : function(state){
26287         state = state === undefined ? !this.pressed : state;
26288         if(state != this.pressed){
26289             if(state){
26290                 this.el.addClass("x-btn-pressed");
26291                 this.pressed = true;
26292                 this.fireEvent("toggle", this, true);
26293             }else{
26294                 this.el.removeClass("x-btn-pressed");
26295                 this.pressed = false;
26296                 this.fireEvent("toggle", this, false);
26297             }
26298             if(this.toggleHandler){
26299                 this.toggleHandler.call(this.scope || this, this, state);
26300             }
26301         }
26302     },
26303     
26304     /**
26305      * Focus the button
26306      */
26307     focus : function(){
26308         this.el.child('button:first').focus();
26309     },
26310     
26311     /**
26312      * Disable this button
26313      */
26314     disable : function(){
26315         if(this.el){
26316             this.el.addClass("x-btn-disabled");
26317         }
26318         this.disabled = true;
26319     },
26320     
26321     /**
26322      * Enable this button
26323      */
26324     enable : function(){
26325         if(this.el){
26326             this.el.removeClass("x-btn-disabled");
26327         }
26328         this.disabled = false;
26329     },
26330
26331     /**
26332      * Convenience function for boolean enable/disable
26333      * @param {Boolean} enabled True to enable, false to disable
26334      */
26335     setDisabled : function(v){
26336         this[v !== true ? "enable" : "disable"]();
26337     },
26338
26339     // private
26340     onClick : function(e){
26341         if(e){
26342             e.preventDefault();
26343         }
26344         if(e.button != 0){
26345             return;
26346         }
26347         if(!this.disabled){
26348             if(this.enableToggle){
26349                 this.toggle();
26350             }
26351             if(this.menu && !this.menu.isVisible()){
26352                 this.menu.show(this.el, this.menuAlign);
26353             }
26354             this.fireEvent("click", this, e);
26355             if(this.handler){
26356                 this.el.removeClass("x-btn-over");
26357                 this.handler.call(this.scope || this, this, e);
26358             }
26359         }
26360     },
26361     // private
26362     onMouseOver : function(e){
26363         if(!this.disabled){
26364             this.el.addClass("x-btn-over");
26365             this.fireEvent('mouseover', this, e);
26366         }
26367     },
26368     // private
26369     onMouseOut : function(e){
26370         if(!e.within(this.el,  true)){
26371             this.el.removeClass("x-btn-over");
26372             this.fireEvent('mouseout', this, e);
26373         }
26374     },
26375     // private
26376     onFocus : function(e){
26377         if(!this.disabled){
26378             this.el.addClass("x-btn-focus");
26379         }
26380     },
26381     // private
26382     onBlur : function(e){
26383         this.el.removeClass("x-btn-focus");
26384     },
26385     // private
26386     onMouseDown : function(e){
26387         if(!this.disabled && e.button == 0){
26388             this.el.addClass("x-btn-click");
26389             Roo.get(document).on('mouseup', this.onMouseUp, this);
26390         }
26391     },
26392     // private
26393     onMouseUp : function(e){
26394         if(e.button == 0){
26395             this.el.removeClass("x-btn-click");
26396             Roo.get(document).un('mouseup', this.onMouseUp, this);
26397         }
26398     },
26399     // private
26400     onMenuShow : function(e){
26401         this.el.addClass("x-btn-menu-active");
26402     },
26403     // private
26404     onMenuHide : function(e){
26405         this.el.removeClass("x-btn-menu-active");
26406     }   
26407 });
26408
26409 // Private utility class used by Button
26410 Roo.ButtonToggleMgr = function(){
26411    var groups = {};
26412    
26413    function toggleGroup(btn, state){
26414        if(state){
26415            var g = groups[btn.toggleGroup];
26416            for(var i = 0, l = g.length; i < l; i++){
26417                if(g[i] != btn){
26418                    g[i].toggle(false);
26419                }
26420            }
26421        }
26422    }
26423    
26424    return {
26425        register : function(btn){
26426            if(!btn.toggleGroup){
26427                return;
26428            }
26429            var g = groups[btn.toggleGroup];
26430            if(!g){
26431                g = groups[btn.toggleGroup] = [];
26432            }
26433            g.push(btn);
26434            btn.on("toggle", toggleGroup);
26435        },
26436        
26437        unregister : function(btn){
26438            if(!btn.toggleGroup){
26439                return;
26440            }
26441            var g = groups[btn.toggleGroup];
26442            if(g){
26443                g.remove(btn);
26444                btn.un("toggle", toggleGroup);
26445            }
26446        }
26447    };
26448 }();/*
26449  * Based on:
26450  * Ext JS Library 1.1.1
26451  * Copyright(c) 2006-2007, Ext JS, LLC.
26452  *
26453  * Originally Released Under LGPL - original licence link has changed is not relivant.
26454  *
26455  * Fork - LGPL
26456  * <script type="text/javascript">
26457  */
26458  
26459 /**
26460  * @class Roo.SplitButton
26461  * @extends Roo.Button
26462  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26463  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26464  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26465  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26466  * @cfg {String} arrowTooltip The title attribute of the arrow
26467  * @constructor
26468  * Create a new menu button
26469  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26470  * @param {Object} config The config object
26471  */
26472 Roo.SplitButton = function(renderTo, config){
26473     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26474     /**
26475      * @event arrowclick
26476      * Fires when this button's arrow is clicked
26477      * @param {SplitButton} this
26478      * @param {EventObject} e The click event
26479      */
26480     this.addEvents({"arrowclick":true});
26481 };
26482
26483 Roo.extend(Roo.SplitButton, Roo.Button, {
26484     render : function(renderTo){
26485         // this is one sweet looking template!
26486         var tpl = new Roo.Template(
26487             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26488             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26489             '<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>',
26490             "</tbody></table></td><td>",
26491             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26492             '<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>',
26493             "</tbody></table></td></tr></table>"
26494         );
26495         var btn = tpl.append(renderTo, [this.text, this.type], true);
26496         var btnEl = btn.child("button");
26497         if(this.cls){
26498             btn.addClass(this.cls);
26499         }
26500         if(this.icon){
26501             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26502         }
26503         if(this.iconCls){
26504             btnEl.addClass(this.iconCls);
26505             if(!this.cls){
26506                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26507             }
26508         }
26509         this.el = btn;
26510         if(this.handleMouseEvents){
26511             btn.on("mouseover", this.onMouseOver, this);
26512             btn.on("mouseout", this.onMouseOut, this);
26513             btn.on("mousedown", this.onMouseDown, this);
26514             btn.on("mouseup", this.onMouseUp, this);
26515         }
26516         btn.on(this.clickEvent, this.onClick, this);
26517         if(this.tooltip){
26518             if(typeof this.tooltip == 'object'){
26519                 Roo.QuickTips.tips(Roo.apply({
26520                       target: btnEl.id
26521                 }, this.tooltip));
26522             } else {
26523                 btnEl.dom[this.tooltipType] = this.tooltip;
26524             }
26525         }
26526         if(this.arrowTooltip){
26527             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26528         }
26529         if(this.hidden){
26530             this.hide();
26531         }
26532         if(this.disabled){
26533             this.disable();
26534         }
26535         if(this.pressed){
26536             this.el.addClass("x-btn-pressed");
26537         }
26538         if(Roo.isIE && !Roo.isIE7){
26539             this.autoWidth.defer(1, this);
26540         }else{
26541             this.autoWidth();
26542         }
26543         if(this.menu){
26544             this.menu.on("show", this.onMenuShow, this);
26545             this.menu.on("hide", this.onMenuHide, this);
26546         }
26547         this.fireEvent('render', this);
26548     },
26549
26550     // private
26551     autoWidth : function(){
26552         if(this.el){
26553             var tbl = this.el.child("table:first");
26554             var tbl2 = this.el.child("table:last");
26555             this.el.setWidth("auto");
26556             tbl.setWidth("auto");
26557             if(Roo.isIE7 && Roo.isStrict){
26558                 var ib = this.el.child('button:first');
26559                 if(ib && ib.getWidth() > 20){
26560                     ib.clip();
26561                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26562                 }
26563             }
26564             if(this.minWidth){
26565                 if(this.hidden){
26566                     this.el.beginMeasure();
26567                 }
26568                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26569                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26570                 }
26571                 if(this.hidden){
26572                     this.el.endMeasure();
26573                 }
26574             }
26575             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26576         } 
26577     },
26578     /**
26579      * Sets this button's click handler
26580      * @param {Function} handler The function to call when the button is clicked
26581      * @param {Object} scope (optional) Scope for the function passed above
26582      */
26583     setHandler : function(handler, scope){
26584         this.handler = handler;
26585         this.scope = scope;  
26586     },
26587     
26588     /**
26589      * Sets this button's arrow click handler
26590      * @param {Function} handler The function to call when the arrow is clicked
26591      * @param {Object} scope (optional) Scope for the function passed above
26592      */
26593     setArrowHandler : function(handler, scope){
26594         this.arrowHandler = handler;
26595         this.scope = scope;  
26596     },
26597     
26598     /**
26599      * Focus the button
26600      */
26601     focus : function(){
26602         if(this.el){
26603             this.el.child("button:first").focus();
26604         }
26605     },
26606
26607     // private
26608     onClick : function(e){
26609         e.preventDefault();
26610         if(!this.disabled){
26611             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26612                 if(this.menu && !this.menu.isVisible()){
26613                     this.menu.show(this.el, this.menuAlign);
26614                 }
26615                 this.fireEvent("arrowclick", this, e);
26616                 if(this.arrowHandler){
26617                     this.arrowHandler.call(this.scope || this, this, e);
26618                 }
26619             }else{
26620                 this.fireEvent("click", this, e);
26621                 if(this.handler){
26622                     this.handler.call(this.scope || this, this, e);
26623                 }
26624             }
26625         }
26626     },
26627     // private
26628     onMouseDown : function(e){
26629         if(!this.disabled){
26630             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26631         }
26632     },
26633     // private
26634     onMouseUp : function(e){
26635         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26636     }   
26637 });
26638
26639
26640 // backwards compat
26641 Roo.MenuButton = Roo.SplitButton;/*
26642  * Based on:
26643  * Ext JS Library 1.1.1
26644  * Copyright(c) 2006-2007, Ext JS, LLC.
26645  *
26646  * Originally Released Under LGPL - original licence link has changed is not relivant.
26647  *
26648  * Fork - LGPL
26649  * <script type="text/javascript">
26650  */
26651
26652 /**
26653  * @class Roo.Toolbar
26654  * Basic Toolbar class.
26655  * @constructor
26656  * Creates a new Toolbar
26657  * @param {Object} config The config object
26658  */ 
26659 Roo.Toolbar = function(container, buttons, config)
26660 {
26661     /// old consturctor format still supported..
26662     if(container instanceof Array){ // omit the container for later rendering
26663         buttons = container;
26664         config = buttons;
26665         container = null;
26666     }
26667     if (typeof(container) == 'object' && container.xtype) {
26668         config = container;
26669         container = config.container;
26670         buttons = config.buttons; // not really - use items!!
26671     }
26672     var xitems = [];
26673     if (config && config.items) {
26674         xitems = config.items;
26675         delete config.items;
26676     }
26677     Roo.apply(this, config);
26678     this.buttons = buttons;
26679     
26680     if(container){
26681         this.render(container);
26682     }
26683     Roo.each(xitems, function(b) {
26684         this.add(b);
26685     }, this);
26686     
26687 };
26688
26689 Roo.Toolbar.prototype = {
26690     /**
26691      * @cfg {Roo.data.Store} items
26692      * array of button configs or elements to add
26693      */
26694     
26695     /**
26696      * @cfg {String/HTMLElement/Element} container
26697      * The id or element that will contain the toolbar
26698      */
26699     // private
26700     render : function(ct){
26701         this.el = Roo.get(ct);
26702         if(this.cls){
26703             this.el.addClass(this.cls);
26704         }
26705         // using a table allows for vertical alignment
26706         // 100% width is needed by Safari...
26707         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26708         this.tr = this.el.child("tr", true);
26709         var autoId = 0;
26710         this.items = new Roo.util.MixedCollection(false, function(o){
26711             return o.id || ("item" + (++autoId));
26712         });
26713         if(this.buttons){
26714             this.add.apply(this, this.buttons);
26715             delete this.buttons;
26716         }
26717     },
26718
26719     /**
26720      * Adds element(s) to the toolbar -- this function takes a variable number of 
26721      * arguments of mixed type and adds them to the toolbar.
26722      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26723      * <ul>
26724      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26725      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26726      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26727      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26728      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26729      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26730      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26731      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26732      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26733      * </ul>
26734      * @param {Mixed} arg2
26735      * @param {Mixed} etc.
26736      */
26737     add : function(){
26738         var a = arguments, l = a.length;
26739         for(var i = 0; i < l; i++){
26740             this._add(a[i]);
26741         }
26742     },
26743     // private..
26744     _add : function(el) {
26745         
26746         if (el.xtype) {
26747             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26748         }
26749         
26750         if (el.applyTo){ // some kind of form field
26751             return this.addField(el);
26752         } 
26753         if (el.render){ // some kind of Toolbar.Item
26754             return this.addItem(el);
26755         }
26756         if (typeof el == "string"){ // string
26757             if(el == "separator" || el == "-"){
26758                 return this.addSeparator();
26759             }
26760             if (el == " "){
26761                 return this.addSpacer();
26762             }
26763             if(el == "->"){
26764                 return this.addFill();
26765             }
26766             return this.addText(el);
26767             
26768         }
26769         if(el.tagName){ // element
26770             return this.addElement(el);
26771         }
26772         if(typeof el == "object"){ // must be button config?
26773             return this.addButton(el);
26774         }
26775         // and now what?!?!
26776         return false;
26777         
26778     },
26779     
26780     /**
26781      * Add an Xtype element
26782      * @param {Object} xtype Xtype Object
26783      * @return {Object} created Object
26784      */
26785     addxtype : function(e){
26786         return this.add(e);  
26787     },
26788     
26789     /**
26790      * Returns the Element for this toolbar.
26791      * @return {Roo.Element}
26792      */
26793     getEl : function(){
26794         return this.el;  
26795     },
26796     
26797     /**
26798      * Adds a separator
26799      * @return {Roo.Toolbar.Item} The separator item
26800      */
26801     addSeparator : function(){
26802         return this.addItem(new Roo.Toolbar.Separator());
26803     },
26804
26805     /**
26806      * Adds a spacer element
26807      * @return {Roo.Toolbar.Spacer} The spacer item
26808      */
26809     addSpacer : function(){
26810         return this.addItem(new Roo.Toolbar.Spacer());
26811     },
26812
26813     /**
26814      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26815      * @return {Roo.Toolbar.Fill} The fill item
26816      */
26817     addFill : function(){
26818         return this.addItem(new Roo.Toolbar.Fill());
26819     },
26820
26821     /**
26822      * Adds any standard HTML element to the toolbar
26823      * @param {String/HTMLElement/Element} el The element or id of the element to add
26824      * @return {Roo.Toolbar.Item} The element's item
26825      */
26826     addElement : function(el){
26827         return this.addItem(new Roo.Toolbar.Item(el));
26828     },
26829     /**
26830      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26831      * @type Roo.util.MixedCollection  
26832      */
26833     items : false,
26834      
26835     /**
26836      * Adds any Toolbar.Item or subclass
26837      * @param {Roo.Toolbar.Item} item
26838      * @return {Roo.Toolbar.Item} The item
26839      */
26840     addItem : function(item){
26841         var td = this.nextBlock();
26842         item.render(td);
26843         this.items.add(item);
26844         return item;
26845     },
26846     
26847     /**
26848      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26849      * @param {Object/Array} config A button config or array of configs
26850      * @return {Roo.Toolbar.Button/Array}
26851      */
26852     addButton : function(config){
26853         if(config instanceof Array){
26854             var buttons = [];
26855             for(var i = 0, len = config.length; i < len; i++) {
26856                 buttons.push(this.addButton(config[i]));
26857             }
26858             return buttons;
26859         }
26860         var b = config;
26861         if(!(config instanceof Roo.Toolbar.Button)){
26862             b = config.split ?
26863                 new Roo.Toolbar.SplitButton(config) :
26864                 new Roo.Toolbar.Button(config);
26865         }
26866         var td = this.nextBlock();
26867         b.render(td);
26868         this.items.add(b);
26869         return b;
26870     },
26871     
26872     /**
26873      * Adds text to the toolbar
26874      * @param {String} text The text to add
26875      * @return {Roo.Toolbar.Item} The element's item
26876      */
26877     addText : function(text){
26878         return this.addItem(new Roo.Toolbar.TextItem(text));
26879     },
26880     
26881     /**
26882      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26883      * @param {Number} index The index where the item is to be inserted
26884      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26885      * @return {Roo.Toolbar.Button/Item}
26886      */
26887     insertButton : function(index, item){
26888         if(item instanceof Array){
26889             var buttons = [];
26890             for(var i = 0, len = item.length; i < len; i++) {
26891                buttons.push(this.insertButton(index + i, item[i]));
26892             }
26893             return buttons;
26894         }
26895         if (!(item instanceof Roo.Toolbar.Button)){
26896            item = new Roo.Toolbar.Button(item);
26897         }
26898         var td = document.createElement("td");
26899         this.tr.insertBefore(td, this.tr.childNodes[index]);
26900         item.render(td);
26901         this.items.insert(index, item);
26902         return item;
26903     },
26904     
26905     /**
26906      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26907      * @param {Object} config
26908      * @return {Roo.Toolbar.Item} The element's item
26909      */
26910     addDom : function(config, returnEl){
26911         var td = this.nextBlock();
26912         Roo.DomHelper.overwrite(td, config);
26913         var ti = new Roo.Toolbar.Item(td.firstChild);
26914         ti.render(td);
26915         this.items.add(ti);
26916         return ti;
26917     },
26918
26919     /**
26920      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26921      * @type Roo.util.MixedCollection  
26922      */
26923     fields : false,
26924     
26925     /**
26926      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26927      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26928      * @param {Roo.form.Field} field
26929      * @return {Roo.ToolbarItem}
26930      */
26931      
26932       
26933     addField : function(field) {
26934         if (!this.fields) {
26935             var autoId = 0;
26936             this.fields = new Roo.util.MixedCollection(false, function(o){
26937                 return o.id || ("item" + (++autoId));
26938             });
26939
26940         }
26941         
26942         var td = this.nextBlock();
26943         field.render(td);
26944         var ti = new Roo.Toolbar.Item(td.firstChild);
26945         ti.render(td);
26946         this.items.add(ti);
26947         this.fields.add(field);
26948         return ti;
26949     },
26950     /**
26951      * Hide the toolbar
26952      * @method hide
26953      */
26954      
26955       
26956     hide : function()
26957     {
26958         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26959         this.el.child('div').hide();
26960     },
26961     /**
26962      * Show the toolbar
26963      * @method show
26964      */
26965     show : function()
26966     {
26967         this.el.child('div').show();
26968     },
26969       
26970     // private
26971     nextBlock : function(){
26972         var td = document.createElement("td");
26973         this.tr.appendChild(td);
26974         return td;
26975     },
26976
26977     // private
26978     destroy : function(){
26979         if(this.items){ // rendered?
26980             Roo.destroy.apply(Roo, this.items.items);
26981         }
26982         if(this.fields){ // rendered?
26983             Roo.destroy.apply(Roo, this.fields.items);
26984         }
26985         Roo.Element.uncache(this.el, this.tr);
26986     }
26987 };
26988
26989 /**
26990  * @class Roo.Toolbar.Item
26991  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26992  * @constructor
26993  * Creates a new Item
26994  * @param {HTMLElement} el 
26995  */
26996 Roo.Toolbar.Item = function(el){
26997     this.el = Roo.getDom(el);
26998     this.id = Roo.id(this.el);
26999     this.hidden = false;
27000 };
27001
27002 Roo.Toolbar.Item.prototype = {
27003     
27004     /**
27005      * Get this item's HTML Element
27006      * @return {HTMLElement}
27007      */
27008     getEl : function(){
27009        return this.el;  
27010     },
27011
27012     // private
27013     render : function(td){
27014         this.td = td;
27015         td.appendChild(this.el);
27016     },
27017     
27018     /**
27019      * Removes and destroys this item.
27020      */
27021     destroy : function(){
27022         this.td.parentNode.removeChild(this.td);
27023     },
27024     
27025     /**
27026      * Shows this item.
27027      */
27028     show: function(){
27029         this.hidden = false;
27030         this.td.style.display = "";
27031     },
27032     
27033     /**
27034      * Hides this item.
27035      */
27036     hide: function(){
27037         this.hidden = true;
27038         this.td.style.display = "none";
27039     },
27040     
27041     /**
27042      * Convenience function for boolean show/hide.
27043      * @param {Boolean} visible true to show/false to hide
27044      */
27045     setVisible: function(visible){
27046         if(visible) {
27047             this.show();
27048         }else{
27049             this.hide();
27050         }
27051     },
27052     
27053     /**
27054      * Try to focus this item.
27055      */
27056     focus : function(){
27057         Roo.fly(this.el).focus();
27058     },
27059     
27060     /**
27061      * Disables this item.
27062      */
27063     disable : function(){
27064         Roo.fly(this.td).addClass("x-item-disabled");
27065         this.disabled = true;
27066         this.el.disabled = true;
27067     },
27068     
27069     /**
27070      * Enables this item.
27071      */
27072     enable : function(){
27073         Roo.fly(this.td).removeClass("x-item-disabled");
27074         this.disabled = false;
27075         this.el.disabled = false;
27076     }
27077 };
27078
27079
27080 /**
27081  * @class Roo.Toolbar.Separator
27082  * @extends Roo.Toolbar.Item
27083  * A simple toolbar separator class
27084  * @constructor
27085  * Creates a new Separator
27086  */
27087 Roo.Toolbar.Separator = function(){
27088     var s = document.createElement("span");
27089     s.className = "ytb-sep";
27090     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27091 };
27092 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27093     enable:Roo.emptyFn,
27094     disable:Roo.emptyFn,
27095     focus:Roo.emptyFn
27096 });
27097
27098 /**
27099  * @class Roo.Toolbar.Spacer
27100  * @extends Roo.Toolbar.Item
27101  * A simple element that adds extra horizontal space to a toolbar.
27102  * @constructor
27103  * Creates a new Spacer
27104  */
27105 Roo.Toolbar.Spacer = function(){
27106     var s = document.createElement("div");
27107     s.className = "ytb-spacer";
27108     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27109 };
27110 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27111     enable:Roo.emptyFn,
27112     disable:Roo.emptyFn,
27113     focus:Roo.emptyFn
27114 });
27115
27116 /**
27117  * @class Roo.Toolbar.Fill
27118  * @extends Roo.Toolbar.Spacer
27119  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27120  * @constructor
27121  * Creates a new Spacer
27122  */
27123 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27124     // private
27125     render : function(td){
27126         td.style.width = '100%';
27127         Roo.Toolbar.Fill.superclass.render.call(this, td);
27128     }
27129 });
27130
27131 /**
27132  * @class Roo.Toolbar.TextItem
27133  * @extends Roo.Toolbar.Item
27134  * A simple class that renders text directly into a toolbar.
27135  * @constructor
27136  * Creates a new TextItem
27137  * @param {String} text
27138  */
27139 Roo.Toolbar.TextItem = function(text){
27140     if (typeof(text) == 'object') {
27141         text = text.text;
27142     }
27143     var s = document.createElement("span");
27144     s.className = "ytb-text";
27145     s.innerHTML = text;
27146     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27147 };
27148 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27149     enable:Roo.emptyFn,
27150     disable:Roo.emptyFn,
27151     focus:Roo.emptyFn
27152 });
27153
27154 /**
27155  * @class Roo.Toolbar.Button
27156  * @extends Roo.Button
27157  * A button that renders into a toolbar.
27158  * @constructor
27159  * Creates a new Button
27160  * @param {Object} config A standard {@link Roo.Button} config object
27161  */
27162 Roo.Toolbar.Button = function(config){
27163     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27164 };
27165 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27166     render : function(td){
27167         this.td = td;
27168         Roo.Toolbar.Button.superclass.render.call(this, td);
27169     },
27170     
27171     /**
27172      * Removes and destroys this button
27173      */
27174     destroy : function(){
27175         Roo.Toolbar.Button.superclass.destroy.call(this);
27176         this.td.parentNode.removeChild(this.td);
27177     },
27178     
27179     /**
27180      * Shows this button
27181      */
27182     show: function(){
27183         this.hidden = false;
27184         this.td.style.display = "";
27185     },
27186     
27187     /**
27188      * Hides this button
27189      */
27190     hide: function(){
27191         this.hidden = true;
27192         this.td.style.display = "none";
27193     },
27194
27195     /**
27196      * Disables this item
27197      */
27198     disable : function(){
27199         Roo.fly(this.td).addClass("x-item-disabled");
27200         this.disabled = true;
27201     },
27202
27203     /**
27204      * Enables this item
27205      */
27206     enable : function(){
27207         Roo.fly(this.td).removeClass("x-item-disabled");
27208         this.disabled = false;
27209     }
27210 });
27211 // backwards compat
27212 Roo.ToolbarButton = Roo.Toolbar.Button;
27213
27214 /**
27215  * @class Roo.Toolbar.SplitButton
27216  * @extends Roo.SplitButton
27217  * A menu button that renders into a toolbar.
27218  * @constructor
27219  * Creates a new SplitButton
27220  * @param {Object} config A standard {@link Roo.SplitButton} config object
27221  */
27222 Roo.Toolbar.SplitButton = function(config){
27223     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27224 };
27225 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27226     render : function(td){
27227         this.td = td;
27228         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27229     },
27230     
27231     /**
27232      * Removes and destroys this button
27233      */
27234     destroy : function(){
27235         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27236         this.td.parentNode.removeChild(this.td);
27237     },
27238     
27239     /**
27240      * Shows this button
27241      */
27242     show: function(){
27243         this.hidden = false;
27244         this.td.style.display = "";
27245     },
27246     
27247     /**
27248      * Hides this button
27249      */
27250     hide: function(){
27251         this.hidden = true;
27252         this.td.style.display = "none";
27253     }
27254 });
27255
27256 // backwards compat
27257 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27258  * Based on:
27259  * Ext JS Library 1.1.1
27260  * Copyright(c) 2006-2007, Ext JS, LLC.
27261  *
27262  * Originally Released Under LGPL - original licence link has changed is not relivant.
27263  *
27264  * Fork - LGPL
27265  * <script type="text/javascript">
27266  */
27267  
27268 /**
27269  * @class Roo.PagingToolbar
27270  * @extends Roo.Toolbar
27271  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27272  * @constructor
27273  * Create a new PagingToolbar
27274  * @param {Object} config The config object
27275  */
27276 Roo.PagingToolbar = function(el, ds, config)
27277 {
27278     // old args format still supported... - xtype is prefered..
27279     if (typeof(el) == 'object' && el.xtype) {
27280         // created from xtype...
27281         config = el;
27282         ds = el.dataSource;
27283         el = config.container;
27284     }
27285     var items = [];
27286     if (config.items) {
27287         items = config.items;
27288         config.items = [];
27289     }
27290     
27291     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27292     this.ds = ds;
27293     this.cursor = 0;
27294     this.renderButtons(this.el);
27295     this.bind(ds);
27296     
27297     // supprot items array.
27298    
27299     Roo.each(items, function(e) {
27300         this.add(Roo.factory(e));
27301     },this);
27302     
27303 };
27304
27305 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27306     /**
27307      * @cfg {Roo.data.Store} dataSource
27308      * The underlying data store providing the paged data
27309      */
27310     /**
27311      * @cfg {String/HTMLElement/Element} container
27312      * container The id or element that will contain the toolbar
27313      */
27314     /**
27315      * @cfg {Boolean} displayInfo
27316      * True to display the displayMsg (defaults to false)
27317      */
27318     /**
27319      * @cfg {Number} pageSize
27320      * The number of records to display per page (defaults to 20)
27321      */
27322     pageSize: 20,
27323     /**
27324      * @cfg {String} displayMsg
27325      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27326      */
27327     displayMsg : 'Displaying {0} - {1} of {2}',
27328     /**
27329      * @cfg {String} emptyMsg
27330      * The message to display when no records are found (defaults to "No data to display")
27331      */
27332     emptyMsg : 'No data to display',
27333     /**
27334      * Customizable piece of the default paging text (defaults to "Page")
27335      * @type String
27336      */
27337     beforePageText : "Page",
27338     /**
27339      * Customizable piece of the default paging text (defaults to "of %0")
27340      * @type String
27341      */
27342     afterPageText : "of {0}",
27343     /**
27344      * Customizable piece of the default paging text (defaults to "First Page")
27345      * @type String
27346      */
27347     firstText : "First Page",
27348     /**
27349      * Customizable piece of the default paging text (defaults to "Previous Page")
27350      * @type String
27351      */
27352     prevText : "Previous Page",
27353     /**
27354      * Customizable piece of the default paging text (defaults to "Next Page")
27355      * @type String
27356      */
27357     nextText : "Next Page",
27358     /**
27359      * Customizable piece of the default paging text (defaults to "Last Page")
27360      * @type String
27361      */
27362     lastText : "Last Page",
27363     /**
27364      * Customizable piece of the default paging text (defaults to "Refresh")
27365      * @type String
27366      */
27367     refreshText : "Refresh",
27368
27369     // private
27370     renderButtons : function(el){
27371         Roo.PagingToolbar.superclass.render.call(this, el);
27372         this.first = this.addButton({
27373             tooltip: this.firstText,
27374             cls: "x-btn-icon x-grid-page-first",
27375             disabled: true,
27376             handler: this.onClick.createDelegate(this, ["first"])
27377         });
27378         this.prev = this.addButton({
27379             tooltip: this.prevText,
27380             cls: "x-btn-icon x-grid-page-prev",
27381             disabled: true,
27382             handler: this.onClick.createDelegate(this, ["prev"])
27383         });
27384         //this.addSeparator();
27385         this.add(this.beforePageText);
27386         this.field = Roo.get(this.addDom({
27387            tag: "input",
27388            type: "text",
27389            size: "3",
27390            value: "1",
27391            cls: "x-grid-page-number"
27392         }).el);
27393         this.field.on("keydown", this.onPagingKeydown, this);
27394         this.field.on("focus", function(){this.dom.select();});
27395         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27396         this.field.setHeight(18);
27397         //this.addSeparator();
27398         this.next = this.addButton({
27399             tooltip: this.nextText,
27400             cls: "x-btn-icon x-grid-page-next",
27401             disabled: true,
27402             handler: this.onClick.createDelegate(this, ["next"])
27403         });
27404         this.last = this.addButton({
27405             tooltip: this.lastText,
27406             cls: "x-btn-icon x-grid-page-last",
27407             disabled: true,
27408             handler: this.onClick.createDelegate(this, ["last"])
27409         });
27410         //this.addSeparator();
27411         this.loading = this.addButton({
27412             tooltip: this.refreshText,
27413             cls: "x-btn-icon x-grid-loading",
27414             handler: this.onClick.createDelegate(this, ["refresh"])
27415         });
27416
27417         if(this.displayInfo){
27418             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27419         }
27420     },
27421
27422     // private
27423     updateInfo : function(){
27424         if(this.displayEl){
27425             var count = this.ds.getCount();
27426             var msg = count == 0 ?
27427                 this.emptyMsg :
27428                 String.format(
27429                     this.displayMsg,
27430                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27431                 );
27432             this.displayEl.update(msg);
27433         }
27434     },
27435
27436     // private
27437     onLoad : function(ds, r, o){
27438        this.cursor = o.params ? o.params.start : 0;
27439        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27440
27441        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27442        this.field.dom.value = ap;
27443        this.first.setDisabled(ap == 1);
27444        this.prev.setDisabled(ap == 1);
27445        this.next.setDisabled(ap == ps);
27446        this.last.setDisabled(ap == ps);
27447        this.loading.enable();
27448        this.updateInfo();
27449     },
27450
27451     // private
27452     getPageData : function(){
27453         var total = this.ds.getTotalCount();
27454         return {
27455             total : total,
27456             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27457             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27458         };
27459     },
27460
27461     // private
27462     onLoadError : function(){
27463         this.loading.enable();
27464     },
27465
27466     // private
27467     onPagingKeydown : function(e){
27468         var k = e.getKey();
27469         var d = this.getPageData();
27470         if(k == e.RETURN){
27471             var v = this.field.dom.value, pageNum;
27472             if(!v || isNaN(pageNum = parseInt(v, 10))){
27473                 this.field.dom.value = d.activePage;
27474                 return;
27475             }
27476             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27477             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27478             e.stopEvent();
27479         }
27480         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))
27481         {
27482           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27483           this.field.dom.value = pageNum;
27484           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27485           e.stopEvent();
27486         }
27487         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27488         {
27489           var v = this.field.dom.value, pageNum; 
27490           var increment = (e.shiftKey) ? 10 : 1;
27491           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27492             increment *= -1;
27493           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27494             this.field.dom.value = d.activePage;
27495             return;
27496           }
27497           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27498           {
27499             this.field.dom.value = parseInt(v, 10) + increment;
27500             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27501             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27502           }
27503           e.stopEvent();
27504         }
27505     },
27506
27507     // private
27508     beforeLoad : function(){
27509         if(this.loading){
27510             this.loading.disable();
27511         }
27512     },
27513
27514     // private
27515     onClick : function(which){
27516         var ds = this.ds;
27517         switch(which){
27518             case "first":
27519                 ds.load({params:{start: 0, limit: this.pageSize}});
27520             break;
27521             case "prev":
27522                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27523             break;
27524             case "next":
27525                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27526             break;
27527             case "last":
27528                 var total = ds.getTotalCount();
27529                 var extra = total % this.pageSize;
27530                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27531                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27532             break;
27533             case "refresh":
27534                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27535             break;
27536         }
27537     },
27538
27539     /**
27540      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27541      * @param {Roo.data.Store} store The data store to unbind
27542      */
27543     unbind : function(ds){
27544         ds.un("beforeload", this.beforeLoad, this);
27545         ds.un("load", this.onLoad, this);
27546         ds.un("loadexception", this.onLoadError, this);
27547         ds.un("remove", this.updateInfo, this);
27548         ds.un("add", this.updateInfo, this);
27549         this.ds = undefined;
27550     },
27551
27552     /**
27553      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27554      * @param {Roo.data.Store} store The data store to bind
27555      */
27556     bind : function(ds){
27557         ds.on("beforeload", this.beforeLoad, this);
27558         ds.on("load", this.onLoad, this);
27559         ds.on("loadexception", this.onLoadError, this);
27560         ds.on("remove", this.updateInfo, this);
27561         ds.on("add", this.updateInfo, this);
27562         this.ds = ds;
27563     }
27564 });/*
27565  * Based on:
27566  * Ext JS Library 1.1.1
27567  * Copyright(c) 2006-2007, Ext JS, LLC.
27568  *
27569  * Originally Released Under LGPL - original licence link has changed is not relivant.
27570  *
27571  * Fork - LGPL
27572  * <script type="text/javascript">
27573  */
27574
27575 /**
27576  * @class Roo.Resizable
27577  * @extends Roo.util.Observable
27578  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27579  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27580  * 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
27581  * the element will be wrapped for you automatically.</p>
27582  * <p>Here is the list of valid resize handles:</p>
27583  * <pre>
27584 Value   Description
27585 ------  -------------------
27586  'n'     north
27587  's'     south
27588  'e'     east
27589  'w'     west
27590  'nw'    northwest
27591  'sw'    southwest
27592  'se'    southeast
27593  'ne'    northeast
27594  'hd'    horizontal drag
27595  'all'   all
27596 </pre>
27597  * <p>Here's an example showing the creation of a typical Resizable:</p>
27598  * <pre><code>
27599 var resizer = new Roo.Resizable("element-id", {
27600     handles: 'all',
27601     minWidth: 200,
27602     minHeight: 100,
27603     maxWidth: 500,
27604     maxHeight: 400,
27605     pinned: true
27606 });
27607 resizer.on("resize", myHandler);
27608 </code></pre>
27609  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27610  * resizer.east.setDisplayed(false);</p>
27611  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27612  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27613  * resize operation's new size (defaults to [0, 0])
27614  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27615  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27616  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27617  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27618  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27619  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27620  * @cfg {Number} width The width of the element in pixels (defaults to null)
27621  * @cfg {Number} height The height of the element in pixels (defaults to null)
27622  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27623  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27624  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27625  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27626  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27627  * in favor of the handles config option (defaults to false)
27628  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27629  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27630  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27631  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27632  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27633  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27634  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27635  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27636  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27637  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27638  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27639  * @constructor
27640  * Create a new resizable component
27641  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27642  * @param {Object} config configuration options
27643   */
27644 Roo.Resizable = function(el, config)
27645 {
27646     this.el = Roo.get(el);
27647
27648     if(config && config.wrap){
27649         config.resizeChild = this.el;
27650         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27651         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27652         this.el.setStyle("overflow", "hidden");
27653         this.el.setPositioning(config.resizeChild.getPositioning());
27654         config.resizeChild.clearPositioning();
27655         if(!config.width || !config.height){
27656             var csize = config.resizeChild.getSize();
27657             this.el.setSize(csize.width, csize.height);
27658         }
27659         if(config.pinned && !config.adjustments){
27660             config.adjustments = "auto";
27661         }
27662     }
27663
27664     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27665     this.proxy.unselectable();
27666     this.proxy.enableDisplayMode('block');
27667
27668     Roo.apply(this, config);
27669
27670     if(this.pinned){
27671         this.disableTrackOver = true;
27672         this.el.addClass("x-resizable-pinned");
27673     }
27674     // if the element isn't positioned, make it relative
27675     var position = this.el.getStyle("position");
27676     if(position != "absolute" && position != "fixed"){
27677         this.el.setStyle("position", "relative");
27678     }
27679     if(!this.handles){ // no handles passed, must be legacy style
27680         this.handles = 's,e,se';
27681         if(this.multiDirectional){
27682             this.handles += ',n,w';
27683         }
27684     }
27685     if(this.handles == "all"){
27686         this.handles = "n s e w ne nw se sw";
27687     }
27688     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27689     var ps = Roo.Resizable.positions;
27690     for(var i = 0, len = hs.length; i < len; i++){
27691         if(hs[i] && ps[hs[i]]){
27692             var pos = ps[hs[i]];
27693             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27694         }
27695     }
27696     // legacy
27697     this.corner = this.southeast;
27698     
27699     // updateBox = the box can move..
27700     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27701         this.updateBox = true;
27702     }
27703
27704     this.activeHandle = null;
27705
27706     if(this.resizeChild){
27707         if(typeof this.resizeChild == "boolean"){
27708             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27709         }else{
27710             this.resizeChild = Roo.get(this.resizeChild, true);
27711         }
27712     }
27713     
27714     if(this.adjustments == "auto"){
27715         var rc = this.resizeChild;
27716         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27717         if(rc && (hw || hn)){
27718             rc.position("relative");
27719             rc.setLeft(hw ? hw.el.getWidth() : 0);
27720             rc.setTop(hn ? hn.el.getHeight() : 0);
27721         }
27722         this.adjustments = [
27723             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27724             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27725         ];
27726     }
27727
27728     if(this.draggable){
27729         this.dd = this.dynamic ?
27730             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27731         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27732     }
27733
27734     // public events
27735     this.addEvents({
27736         /**
27737          * @event beforeresize
27738          * Fired before resize is allowed. Set enabled to false to cancel resize.
27739          * @param {Roo.Resizable} this
27740          * @param {Roo.EventObject} e The mousedown event
27741          */
27742         "beforeresize" : true,
27743         /**
27744          * @event resize
27745          * Fired after a resize.
27746          * @param {Roo.Resizable} this
27747          * @param {Number} width The new width
27748          * @param {Number} height The new height
27749          * @param {Roo.EventObject} e The mouseup event
27750          */
27751         "resize" : true
27752     });
27753
27754     if(this.width !== null && this.height !== null){
27755         this.resizeTo(this.width, this.height);
27756     }else{
27757         this.updateChildSize();
27758     }
27759     if(Roo.isIE){
27760         this.el.dom.style.zoom = 1;
27761     }
27762     Roo.Resizable.superclass.constructor.call(this);
27763 };
27764
27765 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27766         resizeChild : false,
27767         adjustments : [0, 0],
27768         minWidth : 5,
27769         minHeight : 5,
27770         maxWidth : 10000,
27771         maxHeight : 10000,
27772         enabled : true,
27773         animate : false,
27774         duration : .35,
27775         dynamic : false,
27776         handles : false,
27777         multiDirectional : false,
27778         disableTrackOver : false,
27779         easing : 'easeOutStrong',
27780         widthIncrement : 0,
27781         heightIncrement : 0,
27782         pinned : false,
27783         width : null,
27784         height : null,
27785         preserveRatio : false,
27786         transparent: false,
27787         minX: 0,
27788         minY: 0,
27789         draggable: false,
27790
27791         /**
27792          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27793          */
27794         constrainTo: undefined,
27795         /**
27796          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27797          */
27798         resizeRegion: undefined,
27799
27800
27801     /**
27802      * Perform a manual resize
27803      * @param {Number} width
27804      * @param {Number} height
27805      */
27806     resizeTo : function(width, height){
27807         this.el.setSize(width, height);
27808         this.updateChildSize();
27809         this.fireEvent("resize", this, width, height, null);
27810     },
27811
27812     // private
27813     startSizing : function(e, handle){
27814         this.fireEvent("beforeresize", this, e);
27815         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27816
27817             if(!this.overlay){
27818                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27819                 this.overlay.unselectable();
27820                 this.overlay.enableDisplayMode("block");
27821                 this.overlay.on("mousemove", this.onMouseMove, this);
27822                 this.overlay.on("mouseup", this.onMouseUp, this);
27823             }
27824             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27825
27826             this.resizing = true;
27827             this.startBox = this.el.getBox();
27828             this.startPoint = e.getXY();
27829             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27830                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27831
27832             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27833             this.overlay.show();
27834
27835             if(this.constrainTo) {
27836                 var ct = Roo.get(this.constrainTo);
27837                 this.resizeRegion = ct.getRegion().adjust(
27838                     ct.getFrameWidth('t'),
27839                     ct.getFrameWidth('l'),
27840                     -ct.getFrameWidth('b'),
27841                     -ct.getFrameWidth('r')
27842                 );
27843             }
27844
27845             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27846             this.proxy.show();
27847             this.proxy.setBox(this.startBox);
27848             if(!this.dynamic){
27849                 this.proxy.setStyle('visibility', 'visible');
27850             }
27851         }
27852     },
27853
27854     // private
27855     onMouseDown : function(handle, e){
27856         if(this.enabled){
27857             e.stopEvent();
27858             this.activeHandle = handle;
27859             this.startSizing(e, handle);
27860         }
27861     },
27862
27863     // private
27864     onMouseUp : function(e){
27865         var size = this.resizeElement();
27866         this.resizing = false;
27867         this.handleOut();
27868         this.overlay.hide();
27869         this.proxy.hide();
27870         this.fireEvent("resize", this, size.width, size.height, e);
27871     },
27872
27873     // private
27874     updateChildSize : function(){
27875         if(this.resizeChild){
27876             var el = this.el;
27877             var child = this.resizeChild;
27878             var adj = this.adjustments;
27879             if(el.dom.offsetWidth){
27880                 var b = el.getSize(true);
27881                 child.setSize(b.width+adj[0], b.height+adj[1]);
27882             }
27883             // Second call here for IE
27884             // The first call enables instant resizing and
27885             // the second call corrects scroll bars if they
27886             // exist
27887             if(Roo.isIE){
27888                 setTimeout(function(){
27889                     if(el.dom.offsetWidth){
27890                         var b = el.getSize(true);
27891                         child.setSize(b.width+adj[0], b.height+adj[1]);
27892                     }
27893                 }, 10);
27894             }
27895         }
27896     },
27897
27898     // private
27899     snap : function(value, inc, min){
27900         if(!inc || !value) return value;
27901         var newValue = value;
27902         var m = value % inc;
27903         if(m > 0){
27904             if(m > (inc/2)){
27905                 newValue = value + (inc-m);
27906             }else{
27907                 newValue = value - m;
27908             }
27909         }
27910         return Math.max(min, newValue);
27911     },
27912
27913     // private
27914     resizeElement : function(){
27915         var box = this.proxy.getBox();
27916         if(this.updateBox){
27917             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27918         }else{
27919             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27920         }
27921         this.updateChildSize();
27922         if(!this.dynamic){
27923             this.proxy.hide();
27924         }
27925         return box;
27926     },
27927
27928     // private
27929     constrain : function(v, diff, m, mx){
27930         if(v - diff < m){
27931             diff = v - m;
27932         }else if(v - diff > mx){
27933             diff = mx - v;
27934         }
27935         return diff;
27936     },
27937
27938     // private
27939     onMouseMove : function(e){
27940         if(this.enabled){
27941             try{// try catch so if something goes wrong the user doesn't get hung
27942
27943             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27944                 return;
27945             }
27946
27947             //var curXY = this.startPoint;
27948             var curSize = this.curSize || this.startBox;
27949             var x = this.startBox.x, y = this.startBox.y;
27950             var ox = x, oy = y;
27951             var w = curSize.width, h = curSize.height;
27952             var ow = w, oh = h;
27953             var mw = this.minWidth, mh = this.minHeight;
27954             var mxw = this.maxWidth, mxh = this.maxHeight;
27955             var wi = this.widthIncrement;
27956             var hi = this.heightIncrement;
27957
27958             var eventXY = e.getXY();
27959             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27960             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27961
27962             var pos = this.activeHandle.position;
27963
27964             switch(pos){
27965                 case "east":
27966                     w += diffX;
27967                     w = Math.min(Math.max(mw, w), mxw);
27968                     break;
27969              
27970                 case "south":
27971                     h += diffY;
27972                     h = Math.min(Math.max(mh, h), mxh);
27973                     break;
27974                 case "southeast":
27975                     w += diffX;
27976                     h += diffY;
27977                     w = Math.min(Math.max(mw, w), mxw);
27978                     h = Math.min(Math.max(mh, h), mxh);
27979                     break;
27980                 case "north":
27981                     diffY = this.constrain(h, diffY, mh, mxh);
27982                     y += diffY;
27983                     h -= diffY;
27984                     break;
27985                 case "hdrag":
27986                     
27987                     if (wi) {
27988                         var adiffX = Math.abs(diffX);
27989                         var sub = (adiffX % wi); // how much 
27990                         if (sub > (wi/2)) { // far enough to snap
27991                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27992                         } else {
27993                             // remove difference.. 
27994                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27995                         }
27996                     }
27997                     x += diffX;
27998                     x = Math.max(this.minX, x);
27999                     break;
28000                 case "west":
28001                     diffX = this.constrain(w, diffX, mw, mxw);
28002                     x += diffX;
28003                     w -= diffX;
28004                     break;
28005                 case "northeast":
28006                     w += diffX;
28007                     w = Math.min(Math.max(mw, w), mxw);
28008                     diffY = this.constrain(h, diffY, mh, mxh);
28009                     y += diffY;
28010                     h -= diffY;
28011                     break;
28012                 case "northwest":
28013                     diffX = this.constrain(w, diffX, mw, mxw);
28014                     diffY = this.constrain(h, diffY, mh, mxh);
28015                     y += diffY;
28016                     h -= diffY;
28017                     x += diffX;
28018                     w -= diffX;
28019                     break;
28020                case "southwest":
28021                     diffX = this.constrain(w, diffX, mw, mxw);
28022                     h += diffY;
28023                     h = Math.min(Math.max(mh, h), mxh);
28024                     x += diffX;
28025                     w -= diffX;
28026                     break;
28027             }
28028
28029             var sw = this.snap(w, wi, mw);
28030             var sh = this.snap(h, hi, mh);
28031             if(sw != w || sh != h){
28032                 switch(pos){
28033                     case "northeast":
28034                         y -= sh - h;
28035                     break;
28036                     case "north":
28037                         y -= sh - h;
28038                         break;
28039                     case "southwest":
28040                         x -= sw - w;
28041                     break;
28042                     case "west":
28043                         x -= sw - w;
28044                         break;
28045                     case "northwest":
28046                         x -= sw - w;
28047                         y -= sh - h;
28048                     break;
28049                 }
28050                 w = sw;
28051                 h = sh;
28052             }
28053
28054             if(this.preserveRatio){
28055                 switch(pos){
28056                     case "southeast":
28057                     case "east":
28058                         h = oh * (w/ow);
28059                         h = Math.min(Math.max(mh, h), mxh);
28060                         w = ow * (h/oh);
28061                        break;
28062                     case "south":
28063                         w = ow * (h/oh);
28064                         w = Math.min(Math.max(mw, w), mxw);
28065                         h = oh * (w/ow);
28066                         break;
28067                     case "northeast":
28068                         w = ow * (h/oh);
28069                         w = Math.min(Math.max(mw, w), mxw);
28070                         h = oh * (w/ow);
28071                     break;
28072                     case "north":
28073                         var tw = w;
28074                         w = ow * (h/oh);
28075                         w = Math.min(Math.max(mw, w), mxw);
28076                         h = oh * (w/ow);
28077                         x += (tw - w) / 2;
28078                         break;
28079                     case "southwest":
28080                         h = oh * (w/ow);
28081                         h = Math.min(Math.max(mh, h), mxh);
28082                         var tw = w;
28083                         w = ow * (h/oh);
28084                         x += tw - w;
28085                         break;
28086                     case "west":
28087                         var th = h;
28088                         h = oh * (w/ow);
28089                         h = Math.min(Math.max(mh, h), mxh);
28090                         y += (th - h) / 2;
28091                         var tw = w;
28092                         w = ow * (h/oh);
28093                         x += tw - w;
28094                        break;
28095                     case "northwest":
28096                         var tw = w;
28097                         var th = h;
28098                         h = oh * (w/ow);
28099                         h = Math.min(Math.max(mh, h), mxh);
28100                         w = ow * (h/oh);
28101                         y += th - h;
28102                         x += tw - w;
28103                        break;
28104
28105                 }
28106             }
28107             if (pos == 'hdrag') {
28108                 w = ow;
28109             }
28110             this.proxy.setBounds(x, y, w, h);
28111             if(this.dynamic){
28112                 this.resizeElement();
28113             }
28114             }catch(e){}
28115         }
28116     },
28117
28118     // private
28119     handleOver : function(){
28120         if(this.enabled){
28121             this.el.addClass("x-resizable-over");
28122         }
28123     },
28124
28125     // private
28126     handleOut : function(){
28127         if(!this.resizing){
28128             this.el.removeClass("x-resizable-over");
28129         }
28130     },
28131
28132     /**
28133      * Returns the element this component is bound to.
28134      * @return {Roo.Element}
28135      */
28136     getEl : function(){
28137         return this.el;
28138     },
28139
28140     /**
28141      * Returns the resizeChild element (or null).
28142      * @return {Roo.Element}
28143      */
28144     getResizeChild : function(){
28145         return this.resizeChild;
28146     },
28147
28148     /**
28149      * Destroys this resizable. If the element was wrapped and
28150      * removeEl is not true then the element remains.
28151      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28152      */
28153     destroy : function(removeEl){
28154         this.proxy.remove();
28155         if(this.overlay){
28156             this.overlay.removeAllListeners();
28157             this.overlay.remove();
28158         }
28159         var ps = Roo.Resizable.positions;
28160         for(var k in ps){
28161             if(typeof ps[k] != "function" && this[ps[k]]){
28162                 var h = this[ps[k]];
28163                 h.el.removeAllListeners();
28164                 h.el.remove();
28165             }
28166         }
28167         if(removeEl){
28168             this.el.update("");
28169             this.el.remove();
28170         }
28171     }
28172 });
28173
28174 // private
28175 // hash to map config positions to true positions
28176 Roo.Resizable.positions = {
28177     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28178     hd: "hdrag"
28179 };
28180
28181 // private
28182 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28183     if(!this.tpl){
28184         // only initialize the template if resizable is used
28185         var tpl = Roo.DomHelper.createTemplate(
28186             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28187         );
28188         tpl.compile();
28189         Roo.Resizable.Handle.prototype.tpl = tpl;
28190     }
28191     this.position = pos;
28192     this.rz = rz;
28193     // show north drag fro topdra
28194     var handlepos = pos == 'hdrag' ? 'north' : pos;
28195     
28196     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28197     if (pos == 'hdrag') {
28198         this.el.setStyle('cursor', 'pointer');
28199     }
28200     this.el.unselectable();
28201     if(transparent){
28202         this.el.setOpacity(0);
28203     }
28204     this.el.on("mousedown", this.onMouseDown, this);
28205     if(!disableTrackOver){
28206         this.el.on("mouseover", this.onMouseOver, this);
28207         this.el.on("mouseout", this.onMouseOut, this);
28208     }
28209 };
28210
28211 // private
28212 Roo.Resizable.Handle.prototype = {
28213     afterResize : function(rz){
28214         // do nothing
28215     },
28216     // private
28217     onMouseDown : function(e){
28218         this.rz.onMouseDown(this, e);
28219     },
28220     // private
28221     onMouseOver : function(e){
28222         this.rz.handleOver(this, e);
28223     },
28224     // private
28225     onMouseOut : function(e){
28226         this.rz.handleOut(this, e);
28227     }
28228 };/*
28229  * Based on:
28230  * Ext JS Library 1.1.1
28231  * Copyright(c) 2006-2007, Ext JS, LLC.
28232  *
28233  * Originally Released Under LGPL - original licence link has changed is not relivant.
28234  *
28235  * Fork - LGPL
28236  * <script type="text/javascript">
28237  */
28238
28239 /**
28240  * @class Roo.Editor
28241  * @extends Roo.Component
28242  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28243  * @constructor
28244  * Create a new Editor
28245  * @param {Roo.form.Field} field The Field object (or descendant)
28246  * @param {Object} config The config object
28247  */
28248 Roo.Editor = function(field, config){
28249     Roo.Editor.superclass.constructor.call(this, config);
28250     this.field = field;
28251     this.addEvents({
28252         /**
28253              * @event beforestartedit
28254              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28255              * false from the handler of this event.
28256              * @param {Editor} this
28257              * @param {Roo.Element} boundEl The underlying element bound to this editor
28258              * @param {Mixed} value The field value being set
28259              */
28260         "beforestartedit" : true,
28261         /**
28262              * @event startedit
28263              * Fires when this editor is displayed
28264              * @param {Roo.Element} boundEl The underlying element bound to this editor
28265              * @param {Mixed} value The starting field value
28266              */
28267         "startedit" : true,
28268         /**
28269              * @event beforecomplete
28270              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28271              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28272              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28273              * event will not fire since no edit actually occurred.
28274              * @param {Editor} this
28275              * @param {Mixed} value The current field value
28276              * @param {Mixed} startValue The original field value
28277              */
28278         "beforecomplete" : true,
28279         /**
28280              * @event complete
28281              * Fires after editing is complete and any changed value has been written to the underlying field.
28282              * @param {Editor} this
28283              * @param {Mixed} value The current field value
28284              * @param {Mixed} startValue The original field value
28285              */
28286         "complete" : true,
28287         /**
28288          * @event specialkey
28289          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28290          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28291          * @param {Roo.form.Field} this
28292          * @param {Roo.EventObject} e The event object
28293          */
28294         "specialkey" : true
28295     });
28296 };
28297
28298 Roo.extend(Roo.Editor, Roo.Component, {
28299     /**
28300      * @cfg {Boolean/String} autosize
28301      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28302      * or "height" to adopt the height only (defaults to false)
28303      */
28304     /**
28305      * @cfg {Boolean} revertInvalid
28306      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28307      * validation fails (defaults to true)
28308      */
28309     /**
28310      * @cfg {Boolean} ignoreNoChange
28311      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28312      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28313      * will never be ignored.
28314      */
28315     /**
28316      * @cfg {Boolean} hideEl
28317      * False to keep the bound element visible while the editor is displayed (defaults to true)
28318      */
28319     /**
28320      * @cfg {Mixed} value
28321      * The data value of the underlying field (defaults to "")
28322      */
28323     value : "",
28324     /**
28325      * @cfg {String} alignment
28326      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28327      */
28328     alignment: "c-c?",
28329     /**
28330      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28331      * for bottom-right shadow (defaults to "frame")
28332      */
28333     shadow : "frame",
28334     /**
28335      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28336      */
28337     constrain : false,
28338     /**
28339      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28340      */
28341     completeOnEnter : false,
28342     /**
28343      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28344      */
28345     cancelOnEsc : false,
28346     /**
28347      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28348      */
28349     updateEl : false,
28350
28351     // private
28352     onRender : function(ct, position){
28353         this.el = new Roo.Layer({
28354             shadow: this.shadow,
28355             cls: "x-editor",
28356             parentEl : ct,
28357             shim : this.shim,
28358             shadowOffset:4,
28359             id: this.id,
28360             constrain: this.constrain
28361         });
28362         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28363         if(this.field.msgTarget != 'title'){
28364             this.field.msgTarget = 'qtip';
28365         }
28366         this.field.render(this.el);
28367         if(Roo.isGecko){
28368             this.field.el.dom.setAttribute('autocomplete', 'off');
28369         }
28370         this.field.on("specialkey", this.onSpecialKey, this);
28371         if(this.swallowKeys){
28372             this.field.el.swallowEvent(['keydown','keypress']);
28373         }
28374         this.field.show();
28375         this.field.on("blur", this.onBlur, this);
28376         if(this.field.grow){
28377             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28378         }
28379     },
28380
28381     onSpecialKey : function(field, e)
28382     {
28383         //Roo.log('editor onSpecialKey');
28384         if(this.completeOnEnter && e.getKey() == e.ENTER){
28385             e.stopEvent();
28386             this.completeEdit();
28387             return;
28388         }
28389         // do not fire special key otherwise it might hide close the editor...
28390         if(e.getKey() == e.ENTER){    
28391             return;
28392         }
28393         if(this.cancelOnEsc && e.getKey() == e.ESC){
28394             this.cancelEdit();
28395             return;
28396         } 
28397         this.fireEvent('specialkey', field, e);
28398     
28399     },
28400
28401     /**
28402      * Starts the editing process and shows the editor.
28403      * @param {String/HTMLElement/Element} el The element to edit
28404      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28405       * to the innerHTML of el.
28406      */
28407     startEdit : function(el, value){
28408         if(this.editing){
28409             this.completeEdit();
28410         }
28411         this.boundEl = Roo.get(el);
28412         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28413         if(!this.rendered){
28414             this.render(this.parentEl || document.body);
28415         }
28416         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28417             return;
28418         }
28419         this.startValue = v;
28420         this.field.setValue(v);
28421         if(this.autoSize){
28422             var sz = this.boundEl.getSize();
28423             switch(this.autoSize){
28424                 case "width":
28425                 this.setSize(sz.width,  "");
28426                 break;
28427                 case "height":
28428                 this.setSize("",  sz.height);
28429                 break;
28430                 default:
28431                 this.setSize(sz.width,  sz.height);
28432             }
28433         }
28434         this.el.alignTo(this.boundEl, this.alignment);
28435         this.editing = true;
28436         if(Roo.QuickTips){
28437             Roo.QuickTips.disable();
28438         }
28439         this.show();
28440     },
28441
28442     /**
28443      * Sets the height and width of this editor.
28444      * @param {Number} width The new width
28445      * @param {Number} height The new height
28446      */
28447     setSize : function(w, h){
28448         this.field.setSize(w, h);
28449         if(this.el){
28450             this.el.sync();
28451         }
28452     },
28453
28454     /**
28455      * Realigns the editor to the bound field based on the current alignment config value.
28456      */
28457     realign : function(){
28458         this.el.alignTo(this.boundEl, this.alignment);
28459     },
28460
28461     /**
28462      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28463      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28464      */
28465     completeEdit : function(remainVisible){
28466         if(!this.editing){
28467             return;
28468         }
28469         var v = this.getValue();
28470         if(this.revertInvalid !== false && !this.field.isValid()){
28471             v = this.startValue;
28472             this.cancelEdit(true);
28473         }
28474         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28475             this.editing = false;
28476             this.hide();
28477             return;
28478         }
28479         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28480             this.editing = false;
28481             if(this.updateEl && this.boundEl){
28482                 this.boundEl.update(v);
28483             }
28484             if(remainVisible !== true){
28485                 this.hide();
28486             }
28487             this.fireEvent("complete", this, v, this.startValue);
28488         }
28489     },
28490
28491     // private
28492     onShow : function(){
28493         this.el.show();
28494         if(this.hideEl !== false){
28495             this.boundEl.hide();
28496         }
28497         this.field.show();
28498         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28499             this.fixIEFocus = true;
28500             this.deferredFocus.defer(50, this);
28501         }else{
28502             this.field.focus();
28503         }
28504         this.fireEvent("startedit", this.boundEl, this.startValue);
28505     },
28506
28507     deferredFocus : function(){
28508         if(this.editing){
28509             this.field.focus();
28510         }
28511     },
28512
28513     /**
28514      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28515      * reverted to the original starting value.
28516      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28517      * cancel (defaults to false)
28518      */
28519     cancelEdit : function(remainVisible){
28520         if(this.editing){
28521             this.setValue(this.startValue);
28522             if(remainVisible !== true){
28523                 this.hide();
28524             }
28525         }
28526     },
28527
28528     // private
28529     onBlur : function(){
28530         if(this.allowBlur !== true && this.editing){
28531             this.completeEdit();
28532         }
28533     },
28534
28535     // private
28536     onHide : function(){
28537         if(this.editing){
28538             this.completeEdit();
28539             return;
28540         }
28541         this.field.blur();
28542         if(this.field.collapse){
28543             this.field.collapse();
28544         }
28545         this.el.hide();
28546         if(this.hideEl !== false){
28547             this.boundEl.show();
28548         }
28549         if(Roo.QuickTips){
28550             Roo.QuickTips.enable();
28551         }
28552     },
28553
28554     /**
28555      * Sets the data value of the editor
28556      * @param {Mixed} value Any valid value supported by the underlying field
28557      */
28558     setValue : function(v){
28559         this.field.setValue(v);
28560     },
28561
28562     /**
28563      * Gets the data value of the editor
28564      * @return {Mixed} The data value
28565      */
28566     getValue : function(){
28567         return this.field.getValue();
28568     }
28569 });/*
28570  * Based on:
28571  * Ext JS Library 1.1.1
28572  * Copyright(c) 2006-2007, Ext JS, LLC.
28573  *
28574  * Originally Released Under LGPL - original licence link has changed is not relivant.
28575  *
28576  * Fork - LGPL
28577  * <script type="text/javascript">
28578  */
28579  
28580 /**
28581  * @class Roo.BasicDialog
28582  * @extends Roo.util.Observable
28583  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28584  * <pre><code>
28585 var dlg = new Roo.BasicDialog("my-dlg", {
28586     height: 200,
28587     width: 300,
28588     minHeight: 100,
28589     minWidth: 150,
28590     modal: true,
28591     proxyDrag: true,
28592     shadow: true
28593 });
28594 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28595 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28596 dlg.addButton('Cancel', dlg.hide, dlg);
28597 dlg.show();
28598 </code></pre>
28599   <b>A Dialog should always be a direct child of the body element.</b>
28600  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28601  * @cfg {String} title Default text to display in the title bar (defaults to null)
28602  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28603  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28604  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28605  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28606  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28607  * (defaults to null with no animation)
28608  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28609  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28610  * property for valid values (defaults to 'all')
28611  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28612  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28613  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28614  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28615  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28616  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28617  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28618  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28619  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28620  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28621  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28622  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28623  * draggable = true (defaults to false)
28624  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28625  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28626  * shadow (defaults to false)
28627  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28628  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28629  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28630  * @cfg {Array} buttons Array of buttons
28631  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28632  * @constructor
28633  * Create a new BasicDialog.
28634  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28635  * @param {Object} config Configuration options
28636  */
28637 Roo.BasicDialog = function(el, config){
28638     this.el = Roo.get(el);
28639     var dh = Roo.DomHelper;
28640     if(!this.el && config && config.autoCreate){
28641         if(typeof config.autoCreate == "object"){
28642             if(!config.autoCreate.id){
28643                 config.autoCreate.id = el;
28644             }
28645             this.el = dh.append(document.body,
28646                         config.autoCreate, true);
28647         }else{
28648             this.el = dh.append(document.body,
28649                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28650         }
28651     }
28652     el = this.el;
28653     el.setDisplayed(true);
28654     el.hide = this.hideAction;
28655     this.id = el.id;
28656     el.addClass("x-dlg");
28657
28658     Roo.apply(this, config);
28659
28660     this.proxy = el.createProxy("x-dlg-proxy");
28661     this.proxy.hide = this.hideAction;
28662     this.proxy.setOpacity(.5);
28663     this.proxy.hide();
28664
28665     if(config.width){
28666         el.setWidth(config.width);
28667     }
28668     if(config.height){
28669         el.setHeight(config.height);
28670     }
28671     this.size = el.getSize();
28672     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28673         this.xy = [config.x,config.y];
28674     }else{
28675         this.xy = el.getCenterXY(true);
28676     }
28677     /** The header element @type Roo.Element */
28678     this.header = el.child("> .x-dlg-hd");
28679     /** The body element @type Roo.Element */
28680     this.body = el.child("> .x-dlg-bd");
28681     /** The footer element @type Roo.Element */
28682     this.footer = el.child("> .x-dlg-ft");
28683
28684     if(!this.header){
28685         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28686     }
28687     if(!this.body){
28688         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28689     }
28690
28691     this.header.unselectable();
28692     if(this.title){
28693         this.header.update(this.title);
28694     }
28695     // this element allows the dialog to be focused for keyboard event
28696     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28697     this.focusEl.swallowEvent("click", true);
28698
28699     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28700
28701     // wrap the body and footer for special rendering
28702     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28703     if(this.footer){
28704         this.bwrap.dom.appendChild(this.footer.dom);
28705     }
28706
28707     this.bg = this.el.createChild({
28708         tag: "div", cls:"x-dlg-bg",
28709         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28710     });
28711     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28712
28713
28714     if(this.autoScroll !== false && !this.autoTabs){
28715         this.body.setStyle("overflow", "auto");
28716     }
28717
28718     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28719
28720     if(this.closable !== false){
28721         this.el.addClass("x-dlg-closable");
28722         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28723         this.close.on("click", this.closeClick, this);
28724         this.close.addClassOnOver("x-dlg-close-over");
28725     }
28726     if(this.collapsible !== false){
28727         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28728         this.collapseBtn.on("click", this.collapseClick, this);
28729         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28730         this.header.on("dblclick", this.collapseClick, this);
28731     }
28732     if(this.resizable !== false){
28733         this.el.addClass("x-dlg-resizable");
28734         this.resizer = new Roo.Resizable(el, {
28735             minWidth: this.minWidth || 80,
28736             minHeight:this.minHeight || 80,
28737             handles: this.resizeHandles || "all",
28738             pinned: true
28739         });
28740         this.resizer.on("beforeresize", this.beforeResize, this);
28741         this.resizer.on("resize", this.onResize, this);
28742     }
28743     if(this.draggable !== false){
28744         el.addClass("x-dlg-draggable");
28745         if (!this.proxyDrag) {
28746             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28747         }
28748         else {
28749             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28750         }
28751         dd.setHandleElId(this.header.id);
28752         dd.endDrag = this.endMove.createDelegate(this);
28753         dd.startDrag = this.startMove.createDelegate(this);
28754         dd.onDrag = this.onDrag.createDelegate(this);
28755         dd.scroll = false;
28756         this.dd = dd;
28757     }
28758     if(this.modal){
28759         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28760         this.mask.enableDisplayMode("block");
28761         this.mask.hide();
28762         this.el.addClass("x-dlg-modal");
28763     }
28764     if(this.shadow){
28765         this.shadow = new Roo.Shadow({
28766             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28767             offset : this.shadowOffset
28768         });
28769     }else{
28770         this.shadowOffset = 0;
28771     }
28772     if(Roo.useShims && this.shim !== false){
28773         this.shim = this.el.createShim();
28774         this.shim.hide = this.hideAction;
28775         this.shim.hide();
28776     }else{
28777         this.shim = false;
28778     }
28779     if(this.autoTabs){
28780         this.initTabs();
28781     }
28782     if (this.buttons) { 
28783         var bts= this.buttons;
28784         this.buttons = [];
28785         Roo.each(bts, function(b) {
28786             this.addButton(b);
28787         }, this);
28788     }
28789     
28790     
28791     this.addEvents({
28792         /**
28793          * @event keydown
28794          * Fires when a key is pressed
28795          * @param {Roo.BasicDialog} this
28796          * @param {Roo.EventObject} e
28797          */
28798         "keydown" : true,
28799         /**
28800          * @event move
28801          * Fires when this dialog is moved by the user.
28802          * @param {Roo.BasicDialog} this
28803          * @param {Number} x The new page X
28804          * @param {Number} y The new page Y
28805          */
28806         "move" : true,
28807         /**
28808          * @event resize
28809          * Fires when this dialog is resized by the user.
28810          * @param {Roo.BasicDialog} this
28811          * @param {Number} width The new width
28812          * @param {Number} height The new height
28813          */
28814         "resize" : true,
28815         /**
28816          * @event beforehide
28817          * Fires before this dialog is hidden.
28818          * @param {Roo.BasicDialog} this
28819          */
28820         "beforehide" : true,
28821         /**
28822          * @event hide
28823          * Fires when this dialog is hidden.
28824          * @param {Roo.BasicDialog} this
28825          */
28826         "hide" : true,
28827         /**
28828          * @event beforeshow
28829          * Fires before this dialog is shown.
28830          * @param {Roo.BasicDialog} this
28831          */
28832         "beforeshow" : true,
28833         /**
28834          * @event show
28835          * Fires when this dialog is shown.
28836          * @param {Roo.BasicDialog} this
28837          */
28838         "show" : true
28839     });
28840     el.on("keydown", this.onKeyDown, this);
28841     el.on("mousedown", this.toFront, this);
28842     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28843     this.el.hide();
28844     Roo.DialogManager.register(this);
28845     Roo.BasicDialog.superclass.constructor.call(this);
28846 };
28847
28848 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28849     shadowOffset: Roo.isIE ? 6 : 5,
28850     minHeight: 80,
28851     minWidth: 200,
28852     minButtonWidth: 75,
28853     defaultButton: null,
28854     buttonAlign: "right",
28855     tabTag: 'div',
28856     firstShow: true,
28857
28858     /**
28859      * Sets the dialog title text
28860      * @param {String} text The title text to display
28861      * @return {Roo.BasicDialog} this
28862      */
28863     setTitle : function(text){
28864         this.header.update(text);
28865         return this;
28866     },
28867
28868     // private
28869     closeClick : function(){
28870         this.hide();
28871     },
28872
28873     // private
28874     collapseClick : function(){
28875         this[this.collapsed ? "expand" : "collapse"]();
28876     },
28877
28878     /**
28879      * Collapses the dialog to its minimized state (only the title bar is visible).
28880      * Equivalent to the user clicking the collapse dialog button.
28881      */
28882     collapse : function(){
28883         if(!this.collapsed){
28884             this.collapsed = true;
28885             this.el.addClass("x-dlg-collapsed");
28886             this.restoreHeight = this.el.getHeight();
28887             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28888         }
28889     },
28890
28891     /**
28892      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28893      * clicking the expand dialog button.
28894      */
28895     expand : function(){
28896         if(this.collapsed){
28897             this.collapsed = false;
28898             this.el.removeClass("x-dlg-collapsed");
28899             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28900         }
28901     },
28902
28903     /**
28904      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28905      * @return {Roo.TabPanel} The tabs component
28906      */
28907     initTabs : function(){
28908         var tabs = this.getTabs();
28909         while(tabs.getTab(0)){
28910             tabs.removeTab(0);
28911         }
28912         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28913             var dom = el.dom;
28914             tabs.addTab(Roo.id(dom), dom.title);
28915             dom.title = "";
28916         });
28917         tabs.activate(0);
28918         return tabs;
28919     },
28920
28921     // private
28922     beforeResize : function(){
28923         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28924     },
28925
28926     // private
28927     onResize : function(){
28928         this.refreshSize();
28929         this.syncBodyHeight();
28930         this.adjustAssets();
28931         this.focus();
28932         this.fireEvent("resize", this, this.size.width, this.size.height);
28933     },
28934
28935     // private
28936     onKeyDown : function(e){
28937         if(this.isVisible()){
28938             this.fireEvent("keydown", this, e);
28939         }
28940     },
28941
28942     /**
28943      * Resizes the dialog.
28944      * @param {Number} width
28945      * @param {Number} height
28946      * @return {Roo.BasicDialog} this
28947      */
28948     resizeTo : function(width, height){
28949         this.el.setSize(width, height);
28950         this.size = {width: width, height: height};
28951         this.syncBodyHeight();
28952         if(this.fixedcenter){
28953             this.center();
28954         }
28955         if(this.isVisible()){
28956             this.constrainXY();
28957             this.adjustAssets();
28958         }
28959         this.fireEvent("resize", this, width, height);
28960         return this;
28961     },
28962
28963
28964     /**
28965      * Resizes the dialog to fit the specified content size.
28966      * @param {Number} width
28967      * @param {Number} height
28968      * @return {Roo.BasicDialog} this
28969      */
28970     setContentSize : function(w, h){
28971         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28972         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28973         //if(!this.el.isBorderBox()){
28974             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28975             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28976         //}
28977         if(this.tabs){
28978             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28979             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28980         }
28981         this.resizeTo(w, h);
28982         return this;
28983     },
28984
28985     /**
28986      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28987      * executed in response to a particular key being pressed while the dialog is active.
28988      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28989      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28990      * @param {Function} fn The function to call
28991      * @param {Object} scope (optional) The scope of the function
28992      * @return {Roo.BasicDialog} this
28993      */
28994     addKeyListener : function(key, fn, scope){
28995         var keyCode, shift, ctrl, alt;
28996         if(typeof key == "object" && !(key instanceof Array)){
28997             keyCode = key["key"];
28998             shift = key["shift"];
28999             ctrl = key["ctrl"];
29000             alt = key["alt"];
29001         }else{
29002             keyCode = key;
29003         }
29004         var handler = function(dlg, e){
29005             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29006                 var k = e.getKey();
29007                 if(keyCode instanceof Array){
29008                     for(var i = 0, len = keyCode.length; i < len; i++){
29009                         if(keyCode[i] == k){
29010                           fn.call(scope || window, dlg, k, e);
29011                           return;
29012                         }
29013                     }
29014                 }else{
29015                     if(k == keyCode){
29016                         fn.call(scope || window, dlg, k, e);
29017                     }
29018                 }
29019             }
29020         };
29021         this.on("keydown", handler);
29022         return this;
29023     },
29024
29025     /**
29026      * Returns the TabPanel component (creates it if it doesn't exist).
29027      * Note: If you wish to simply check for the existence of tabs without creating them,
29028      * check for a null 'tabs' property.
29029      * @return {Roo.TabPanel} The tabs component
29030      */
29031     getTabs : function(){
29032         if(!this.tabs){
29033             this.el.addClass("x-dlg-auto-tabs");
29034             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29035             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29036         }
29037         return this.tabs;
29038     },
29039
29040     /**
29041      * Adds a button to the footer section of the dialog.
29042      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29043      * object or a valid Roo.DomHelper element config
29044      * @param {Function} handler The function called when the button is clicked
29045      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29046      * @return {Roo.Button} The new button
29047      */
29048     addButton : function(config, handler, scope){
29049         var dh = Roo.DomHelper;
29050         if(!this.footer){
29051             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29052         }
29053         if(!this.btnContainer){
29054             var tb = this.footer.createChild({
29055
29056                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29057                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29058             }, null, true);
29059             this.btnContainer = tb.firstChild.firstChild.firstChild;
29060         }
29061         var bconfig = {
29062             handler: handler,
29063             scope: scope,
29064             minWidth: this.minButtonWidth,
29065             hideParent:true
29066         };
29067         if(typeof config == "string"){
29068             bconfig.text = config;
29069         }else{
29070             if(config.tag){
29071                 bconfig.dhconfig = config;
29072             }else{
29073                 Roo.apply(bconfig, config);
29074             }
29075         }
29076         var fc = false;
29077         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29078             bconfig.position = Math.max(0, bconfig.position);
29079             fc = this.btnContainer.childNodes[bconfig.position];
29080         }
29081          
29082         var btn = new Roo.Button(
29083             fc ? 
29084                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29085                 : this.btnContainer.appendChild(document.createElement("td")),
29086             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29087             bconfig
29088         );
29089         this.syncBodyHeight();
29090         if(!this.buttons){
29091             /**
29092              * Array of all the buttons that have been added to this dialog via addButton
29093              * @type Array
29094              */
29095             this.buttons = [];
29096         }
29097         this.buttons.push(btn);
29098         return btn;
29099     },
29100
29101     /**
29102      * Sets the default button to be focused when the dialog is displayed.
29103      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29104      * @return {Roo.BasicDialog} this
29105      */
29106     setDefaultButton : function(btn){
29107         this.defaultButton = btn;
29108         return this;
29109     },
29110
29111     // private
29112     getHeaderFooterHeight : function(safe){
29113         var height = 0;
29114         if(this.header){
29115            height += this.header.getHeight();
29116         }
29117         if(this.footer){
29118            var fm = this.footer.getMargins();
29119             height += (this.footer.getHeight()+fm.top+fm.bottom);
29120         }
29121         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29122         height += this.centerBg.getPadding("tb");
29123         return height;
29124     },
29125
29126     // private
29127     syncBodyHeight : function(){
29128         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29129         var height = this.size.height - this.getHeaderFooterHeight(false);
29130         bd.setHeight(height-bd.getMargins("tb"));
29131         var hh = this.header.getHeight();
29132         var h = this.size.height-hh;
29133         cb.setHeight(h);
29134         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29135         bw.setHeight(h-cb.getPadding("tb"));
29136         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29137         bd.setWidth(bw.getWidth(true));
29138         if(this.tabs){
29139             this.tabs.syncHeight();
29140             if(Roo.isIE){
29141                 this.tabs.el.repaint();
29142             }
29143         }
29144     },
29145
29146     /**
29147      * Restores the previous state of the dialog if Roo.state is configured.
29148      * @return {Roo.BasicDialog} this
29149      */
29150     restoreState : function(){
29151         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29152         if(box && box.width){
29153             this.xy = [box.x, box.y];
29154             this.resizeTo(box.width, box.height);
29155         }
29156         return this;
29157     },
29158
29159     // private
29160     beforeShow : function(){
29161         this.expand();
29162         if(this.fixedcenter){
29163             this.xy = this.el.getCenterXY(true);
29164         }
29165         if(this.modal){
29166             Roo.get(document.body).addClass("x-body-masked");
29167             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29168             this.mask.show();
29169         }
29170         this.constrainXY();
29171     },
29172
29173     // private
29174     animShow : function(){
29175         var b = Roo.get(this.animateTarget).getBox();
29176         this.proxy.setSize(b.width, b.height);
29177         this.proxy.setLocation(b.x, b.y);
29178         this.proxy.show();
29179         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29180                     true, .35, this.showEl.createDelegate(this));
29181     },
29182
29183     /**
29184      * Shows the dialog.
29185      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29186      * @return {Roo.BasicDialog} this
29187      */
29188     show : function(animateTarget){
29189         if (this.fireEvent("beforeshow", this) === false){
29190             return;
29191         }
29192         if(this.syncHeightBeforeShow){
29193             this.syncBodyHeight();
29194         }else if(this.firstShow){
29195             this.firstShow = false;
29196             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29197         }
29198         this.animateTarget = animateTarget || this.animateTarget;
29199         if(!this.el.isVisible()){
29200             this.beforeShow();
29201             if(this.animateTarget && Roo.get(this.animateTarget)){
29202                 this.animShow();
29203             }else{
29204                 this.showEl();
29205             }
29206         }
29207         return this;
29208     },
29209
29210     // private
29211     showEl : function(){
29212         this.proxy.hide();
29213         this.el.setXY(this.xy);
29214         this.el.show();
29215         this.adjustAssets(true);
29216         this.toFront();
29217         this.focus();
29218         // IE peekaboo bug - fix found by Dave Fenwick
29219         if(Roo.isIE){
29220             this.el.repaint();
29221         }
29222         this.fireEvent("show", this);
29223     },
29224
29225     /**
29226      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29227      * dialog itself will receive focus.
29228      */
29229     focus : function(){
29230         if(this.defaultButton){
29231             this.defaultButton.focus();
29232         }else{
29233             this.focusEl.focus();
29234         }
29235     },
29236
29237     // private
29238     constrainXY : function(){
29239         if(this.constraintoviewport !== false){
29240             if(!this.viewSize){
29241                 if(this.container){
29242                     var s = this.container.getSize();
29243                     this.viewSize = [s.width, s.height];
29244                 }else{
29245                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29246                 }
29247             }
29248             var s = Roo.get(this.container||document).getScroll();
29249
29250             var x = this.xy[0], y = this.xy[1];
29251             var w = this.size.width, h = this.size.height;
29252             var vw = this.viewSize[0], vh = this.viewSize[1];
29253             // only move it if it needs it
29254             var moved = false;
29255             // first validate right/bottom
29256             if(x + w > vw+s.left){
29257                 x = vw - w;
29258                 moved = true;
29259             }
29260             if(y + h > vh+s.top){
29261                 y = vh - h;
29262                 moved = true;
29263             }
29264             // then make sure top/left isn't negative
29265             if(x < s.left){
29266                 x = s.left;
29267                 moved = true;
29268             }
29269             if(y < s.top){
29270                 y = s.top;
29271                 moved = true;
29272             }
29273             if(moved){
29274                 // cache xy
29275                 this.xy = [x, y];
29276                 if(this.isVisible()){
29277                     this.el.setLocation(x, y);
29278                     this.adjustAssets();
29279                 }
29280             }
29281         }
29282     },
29283
29284     // private
29285     onDrag : function(){
29286         if(!this.proxyDrag){
29287             this.xy = this.el.getXY();
29288             this.adjustAssets();
29289         }
29290     },
29291
29292     // private
29293     adjustAssets : function(doShow){
29294         var x = this.xy[0], y = this.xy[1];
29295         var w = this.size.width, h = this.size.height;
29296         if(doShow === true){
29297             if(this.shadow){
29298                 this.shadow.show(this.el);
29299             }
29300             if(this.shim){
29301                 this.shim.show();
29302             }
29303         }
29304         if(this.shadow && this.shadow.isVisible()){
29305             this.shadow.show(this.el);
29306         }
29307         if(this.shim && this.shim.isVisible()){
29308             this.shim.setBounds(x, y, w, h);
29309         }
29310     },
29311
29312     // private
29313     adjustViewport : function(w, h){
29314         if(!w || !h){
29315             w = Roo.lib.Dom.getViewWidth();
29316             h = Roo.lib.Dom.getViewHeight();
29317         }
29318         // cache the size
29319         this.viewSize = [w, h];
29320         if(this.modal && this.mask.isVisible()){
29321             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29322             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29323         }
29324         if(this.isVisible()){
29325             this.constrainXY();
29326         }
29327     },
29328
29329     /**
29330      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29331      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29332      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29333      */
29334     destroy : function(removeEl){
29335         if(this.isVisible()){
29336             this.animateTarget = null;
29337             this.hide();
29338         }
29339         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29340         if(this.tabs){
29341             this.tabs.destroy(removeEl);
29342         }
29343         Roo.destroy(
29344              this.shim,
29345              this.proxy,
29346              this.resizer,
29347              this.close,
29348              this.mask
29349         );
29350         if(this.dd){
29351             this.dd.unreg();
29352         }
29353         if(this.buttons){
29354            for(var i = 0, len = this.buttons.length; i < len; i++){
29355                this.buttons[i].destroy();
29356            }
29357         }
29358         this.el.removeAllListeners();
29359         if(removeEl === true){
29360             this.el.update("");
29361             this.el.remove();
29362         }
29363         Roo.DialogManager.unregister(this);
29364     },
29365
29366     // private
29367     startMove : function(){
29368         if(this.proxyDrag){
29369             this.proxy.show();
29370         }
29371         if(this.constraintoviewport !== false){
29372             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29373         }
29374     },
29375
29376     // private
29377     endMove : function(){
29378         if(!this.proxyDrag){
29379             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29380         }else{
29381             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29382             this.proxy.hide();
29383         }
29384         this.refreshSize();
29385         this.adjustAssets();
29386         this.focus();
29387         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29388     },
29389
29390     /**
29391      * Brings this dialog to the front of any other visible dialogs
29392      * @return {Roo.BasicDialog} this
29393      */
29394     toFront : function(){
29395         Roo.DialogManager.bringToFront(this);
29396         return this;
29397     },
29398
29399     /**
29400      * Sends this dialog to the back (under) of any other visible dialogs
29401      * @return {Roo.BasicDialog} this
29402      */
29403     toBack : function(){
29404         Roo.DialogManager.sendToBack(this);
29405         return this;
29406     },
29407
29408     /**
29409      * Centers this dialog in the viewport
29410      * @return {Roo.BasicDialog} this
29411      */
29412     center : function(){
29413         var xy = this.el.getCenterXY(true);
29414         this.moveTo(xy[0], xy[1]);
29415         return this;
29416     },
29417
29418     /**
29419      * Moves the dialog's top-left corner to the specified point
29420      * @param {Number} x
29421      * @param {Number} y
29422      * @return {Roo.BasicDialog} this
29423      */
29424     moveTo : function(x, y){
29425         this.xy = [x,y];
29426         if(this.isVisible()){
29427             this.el.setXY(this.xy);
29428             this.adjustAssets();
29429         }
29430         return this;
29431     },
29432
29433     /**
29434      * Aligns the dialog to the specified element
29435      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29436      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29437      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29438      * @return {Roo.BasicDialog} this
29439      */
29440     alignTo : function(element, position, offsets){
29441         this.xy = this.el.getAlignToXY(element, position, offsets);
29442         if(this.isVisible()){
29443             this.el.setXY(this.xy);
29444             this.adjustAssets();
29445         }
29446         return this;
29447     },
29448
29449     /**
29450      * Anchors an element to another element and realigns it when the window is resized.
29451      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29452      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29453      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29454      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29455      * is a number, it is used as the buffer delay (defaults to 50ms).
29456      * @return {Roo.BasicDialog} this
29457      */
29458     anchorTo : function(el, alignment, offsets, monitorScroll){
29459         var action = function(){
29460             this.alignTo(el, alignment, offsets);
29461         };
29462         Roo.EventManager.onWindowResize(action, this);
29463         var tm = typeof monitorScroll;
29464         if(tm != 'undefined'){
29465             Roo.EventManager.on(window, 'scroll', action, this,
29466                 {buffer: tm == 'number' ? monitorScroll : 50});
29467         }
29468         action.call(this);
29469         return this;
29470     },
29471
29472     /**
29473      * Returns true if the dialog is visible
29474      * @return {Boolean}
29475      */
29476     isVisible : function(){
29477         return this.el.isVisible();
29478     },
29479
29480     // private
29481     animHide : function(callback){
29482         var b = Roo.get(this.animateTarget).getBox();
29483         this.proxy.show();
29484         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29485         this.el.hide();
29486         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29487                     this.hideEl.createDelegate(this, [callback]));
29488     },
29489
29490     /**
29491      * Hides the dialog.
29492      * @param {Function} callback (optional) Function to call when the dialog is hidden
29493      * @return {Roo.BasicDialog} this
29494      */
29495     hide : function(callback){
29496         if (this.fireEvent("beforehide", this) === false){
29497             return;
29498         }
29499         if(this.shadow){
29500             this.shadow.hide();
29501         }
29502         if(this.shim) {
29503           this.shim.hide();
29504         }
29505         // sometimes animateTarget seems to get set.. causing problems...
29506         // this just double checks..
29507         if(this.animateTarget && Roo.get(this.animateTarget)) {
29508            this.animHide(callback);
29509         }else{
29510             this.el.hide();
29511             this.hideEl(callback);
29512         }
29513         return this;
29514     },
29515
29516     // private
29517     hideEl : function(callback){
29518         this.proxy.hide();
29519         if(this.modal){
29520             this.mask.hide();
29521             Roo.get(document.body).removeClass("x-body-masked");
29522         }
29523         this.fireEvent("hide", this);
29524         if(typeof callback == "function"){
29525             callback();
29526         }
29527     },
29528
29529     // private
29530     hideAction : function(){
29531         this.setLeft("-10000px");
29532         this.setTop("-10000px");
29533         this.setStyle("visibility", "hidden");
29534     },
29535
29536     // private
29537     refreshSize : function(){
29538         this.size = this.el.getSize();
29539         this.xy = this.el.getXY();
29540         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29541     },
29542
29543     // private
29544     // z-index is managed by the DialogManager and may be overwritten at any time
29545     setZIndex : function(index){
29546         if(this.modal){
29547             this.mask.setStyle("z-index", index);
29548         }
29549         if(this.shim){
29550             this.shim.setStyle("z-index", ++index);
29551         }
29552         if(this.shadow){
29553             this.shadow.setZIndex(++index);
29554         }
29555         this.el.setStyle("z-index", ++index);
29556         if(this.proxy){
29557             this.proxy.setStyle("z-index", ++index);
29558         }
29559         if(this.resizer){
29560             this.resizer.proxy.setStyle("z-index", ++index);
29561         }
29562
29563         this.lastZIndex = index;
29564     },
29565
29566     /**
29567      * Returns the element for this dialog
29568      * @return {Roo.Element} The underlying dialog Element
29569      */
29570     getEl : function(){
29571         return this.el;
29572     }
29573 });
29574
29575 /**
29576  * @class Roo.DialogManager
29577  * Provides global access to BasicDialogs that have been created and
29578  * support for z-indexing (layering) multiple open dialogs.
29579  */
29580 Roo.DialogManager = function(){
29581     var list = {};
29582     var accessList = [];
29583     var front = null;
29584
29585     // private
29586     var sortDialogs = function(d1, d2){
29587         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29588     };
29589
29590     // private
29591     var orderDialogs = function(){
29592         accessList.sort(sortDialogs);
29593         var seed = Roo.DialogManager.zseed;
29594         for(var i = 0, len = accessList.length; i < len; i++){
29595             var dlg = accessList[i];
29596             if(dlg){
29597                 dlg.setZIndex(seed + (i*10));
29598             }
29599         }
29600     };
29601
29602     return {
29603         /**
29604          * The starting z-index for BasicDialogs (defaults to 9000)
29605          * @type Number The z-index value
29606          */
29607         zseed : 9000,
29608
29609         // private
29610         register : function(dlg){
29611             list[dlg.id] = dlg;
29612             accessList.push(dlg);
29613         },
29614
29615         // private
29616         unregister : function(dlg){
29617             delete list[dlg.id];
29618             var i=0;
29619             var len=0;
29620             if(!accessList.indexOf){
29621                 for(  i = 0, len = accessList.length; i < len; i++){
29622                     if(accessList[i] == dlg){
29623                         accessList.splice(i, 1);
29624                         return;
29625                     }
29626                 }
29627             }else{
29628                  i = accessList.indexOf(dlg);
29629                 if(i != -1){
29630                     accessList.splice(i, 1);
29631                 }
29632             }
29633         },
29634
29635         /**
29636          * Gets a registered dialog by id
29637          * @param {String/Object} id The id of the dialog or a dialog
29638          * @return {Roo.BasicDialog} this
29639          */
29640         get : function(id){
29641             return typeof id == "object" ? id : list[id];
29642         },
29643
29644         /**
29645          * Brings the specified dialog to the front
29646          * @param {String/Object} dlg The id of the dialog or a dialog
29647          * @return {Roo.BasicDialog} this
29648          */
29649         bringToFront : function(dlg){
29650             dlg = this.get(dlg);
29651             if(dlg != front){
29652                 front = dlg;
29653                 dlg._lastAccess = new Date().getTime();
29654                 orderDialogs();
29655             }
29656             return dlg;
29657         },
29658
29659         /**
29660          * Sends the specified dialog to the back
29661          * @param {String/Object} dlg The id of the dialog or a dialog
29662          * @return {Roo.BasicDialog} this
29663          */
29664         sendToBack : function(dlg){
29665             dlg = this.get(dlg);
29666             dlg._lastAccess = -(new Date().getTime());
29667             orderDialogs();
29668             return dlg;
29669         },
29670
29671         /**
29672          * Hides all dialogs
29673          */
29674         hideAll : function(){
29675             for(var id in list){
29676                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29677                     list[id].hide();
29678                 }
29679             }
29680         }
29681     };
29682 }();
29683
29684 /**
29685  * @class Roo.LayoutDialog
29686  * @extends Roo.BasicDialog
29687  * Dialog which provides adjustments for working with a layout in a Dialog.
29688  * Add your necessary layout config options to the dialog's config.<br>
29689  * Example usage (including a nested layout):
29690  * <pre><code>
29691 if(!dialog){
29692     dialog = new Roo.LayoutDialog("download-dlg", {
29693         modal: true,
29694         width:600,
29695         height:450,
29696         shadow:true,
29697         minWidth:500,
29698         minHeight:350,
29699         autoTabs:true,
29700         proxyDrag:true,
29701         // layout config merges with the dialog config
29702         center:{
29703             tabPosition: "top",
29704             alwaysShowTabs: true
29705         }
29706     });
29707     dialog.addKeyListener(27, dialog.hide, dialog);
29708     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29709     dialog.addButton("Build It!", this.getDownload, this);
29710
29711     // we can even add nested layouts
29712     var innerLayout = new Roo.BorderLayout("dl-inner", {
29713         east: {
29714             initialSize: 200,
29715             autoScroll:true,
29716             split:true
29717         },
29718         center: {
29719             autoScroll:true
29720         }
29721     });
29722     innerLayout.beginUpdate();
29723     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29724     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29725     innerLayout.endUpdate(true);
29726
29727     var layout = dialog.getLayout();
29728     layout.beginUpdate();
29729     layout.add("center", new Roo.ContentPanel("standard-panel",
29730                         {title: "Download the Source", fitToFrame:true}));
29731     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29732                {title: "Build your own roo.js"}));
29733     layout.getRegion("center").showPanel(sp);
29734     layout.endUpdate();
29735 }
29736 </code></pre>
29737     * @constructor
29738     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29739     * @param {Object} config configuration options
29740   */
29741 Roo.LayoutDialog = function(el, cfg){
29742     
29743     var config=  cfg;
29744     if (typeof(cfg) == 'undefined') {
29745         config = Roo.apply({}, el);
29746         // not sure why we use documentElement here.. - it should always be body.
29747         // IE7 borks horribly if we use documentElement.
29748         // webkit also does not like documentElement - it creates a body element...
29749         el = Roo.get( document.body || document.documentElement ).createChild();
29750         //config.autoCreate = true;
29751     }
29752     
29753     
29754     config.autoTabs = false;
29755     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29756     this.body.setStyle({overflow:"hidden", position:"relative"});
29757     this.layout = new Roo.BorderLayout(this.body.dom, config);
29758     this.layout.monitorWindowResize = false;
29759     this.el.addClass("x-dlg-auto-layout");
29760     // fix case when center region overwrites center function
29761     this.center = Roo.BasicDialog.prototype.center;
29762     this.on("show", this.layout.layout, this.layout, true);
29763     if (config.items) {
29764         var xitems = config.items;
29765         delete config.items;
29766         Roo.each(xitems, this.addxtype, this);
29767     }
29768     
29769     
29770 };
29771 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29772     /**
29773      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29774      * @deprecated
29775      */
29776     endUpdate : function(){
29777         this.layout.endUpdate();
29778     },
29779
29780     /**
29781      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29782      *  @deprecated
29783      */
29784     beginUpdate : function(){
29785         this.layout.beginUpdate();
29786     },
29787
29788     /**
29789      * Get the BorderLayout for this dialog
29790      * @return {Roo.BorderLayout}
29791      */
29792     getLayout : function(){
29793         return this.layout;
29794     },
29795
29796     showEl : function(){
29797         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29798         if(Roo.isIE7){
29799             this.layout.layout();
29800         }
29801     },
29802
29803     // private
29804     // Use the syncHeightBeforeShow config option to control this automatically
29805     syncBodyHeight : function(){
29806         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29807         if(this.layout){this.layout.layout();}
29808     },
29809     
29810       /**
29811      * Add an xtype element (actually adds to the layout.)
29812      * @return {Object} xdata xtype object data.
29813      */
29814     
29815     addxtype : function(c) {
29816         return this.layout.addxtype(c);
29817     }
29818 });/*
29819  * Based on:
29820  * Ext JS Library 1.1.1
29821  * Copyright(c) 2006-2007, Ext JS, LLC.
29822  *
29823  * Originally Released Under LGPL - original licence link has changed is not relivant.
29824  *
29825  * Fork - LGPL
29826  * <script type="text/javascript">
29827  */
29828  
29829 /**
29830  * @class Roo.MessageBox
29831  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29832  * Example usage:
29833  *<pre><code>
29834 // Basic alert:
29835 Roo.Msg.alert('Status', 'Changes saved successfully.');
29836
29837 // Prompt for user data:
29838 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29839     if (btn == 'ok'){
29840         // process text value...
29841     }
29842 });
29843
29844 // Show a dialog using config options:
29845 Roo.Msg.show({
29846    title:'Save Changes?',
29847    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29848    buttons: Roo.Msg.YESNOCANCEL,
29849    fn: processResult,
29850    animEl: 'elId'
29851 });
29852 </code></pre>
29853  * @singleton
29854  */
29855 Roo.MessageBox = function(){
29856     var dlg, opt, mask, waitTimer;
29857     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29858     var buttons, activeTextEl, bwidth;
29859
29860     // private
29861     var handleButton = function(button){
29862         dlg.hide();
29863         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29864     };
29865
29866     // private
29867     var handleHide = function(){
29868         if(opt && opt.cls){
29869             dlg.el.removeClass(opt.cls);
29870         }
29871         if(waitTimer){
29872             Roo.TaskMgr.stop(waitTimer);
29873             waitTimer = null;
29874         }
29875     };
29876
29877     // private
29878     var updateButtons = function(b){
29879         var width = 0;
29880         if(!b){
29881             buttons["ok"].hide();
29882             buttons["cancel"].hide();
29883             buttons["yes"].hide();
29884             buttons["no"].hide();
29885             dlg.footer.dom.style.display = 'none';
29886             return width;
29887         }
29888         dlg.footer.dom.style.display = '';
29889         for(var k in buttons){
29890             if(typeof buttons[k] != "function"){
29891                 if(b[k]){
29892                     buttons[k].show();
29893                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29894                     width += buttons[k].el.getWidth()+15;
29895                 }else{
29896                     buttons[k].hide();
29897                 }
29898             }
29899         }
29900         return width;
29901     };
29902
29903     // private
29904     var handleEsc = function(d, k, e){
29905         if(opt && opt.closable !== false){
29906             dlg.hide();
29907         }
29908         if(e){
29909             e.stopEvent();
29910         }
29911     };
29912
29913     return {
29914         /**
29915          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29916          * @return {Roo.BasicDialog} The BasicDialog element
29917          */
29918         getDialog : function(){
29919            if(!dlg){
29920                 dlg = new Roo.BasicDialog("x-msg-box", {
29921                     autoCreate : true,
29922                     shadow: true,
29923                     draggable: true,
29924                     resizable:false,
29925                     constraintoviewport:false,
29926                     fixedcenter:true,
29927                     collapsible : false,
29928                     shim:true,
29929                     modal: true,
29930                     width:400, height:100,
29931                     buttonAlign:"center",
29932                     closeClick : function(){
29933                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29934                             handleButton("no");
29935                         }else{
29936                             handleButton("cancel");
29937                         }
29938                     }
29939                 });
29940                 dlg.on("hide", handleHide);
29941                 mask = dlg.mask;
29942                 dlg.addKeyListener(27, handleEsc);
29943                 buttons = {};
29944                 var bt = this.buttonText;
29945                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29946                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29947                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29948                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29949                 bodyEl = dlg.body.createChild({
29950
29951                     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>'
29952                 });
29953                 msgEl = bodyEl.dom.firstChild;
29954                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29955                 textboxEl.enableDisplayMode();
29956                 textboxEl.addKeyListener([10,13], function(){
29957                     if(dlg.isVisible() && opt && opt.buttons){
29958                         if(opt.buttons.ok){
29959                             handleButton("ok");
29960                         }else if(opt.buttons.yes){
29961                             handleButton("yes");
29962                         }
29963                     }
29964                 });
29965                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29966                 textareaEl.enableDisplayMode();
29967                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29968                 progressEl.enableDisplayMode();
29969                 var pf = progressEl.dom.firstChild;
29970                 if (pf) {
29971                     pp = Roo.get(pf.firstChild);
29972                     pp.setHeight(pf.offsetHeight);
29973                 }
29974                 
29975             }
29976             return dlg;
29977         },
29978
29979         /**
29980          * Updates the message box body text
29981          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29982          * the XHTML-compliant non-breaking space character '&amp;#160;')
29983          * @return {Roo.MessageBox} This message box
29984          */
29985         updateText : function(text){
29986             if(!dlg.isVisible() && !opt.width){
29987                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29988             }
29989             msgEl.innerHTML = text || '&#160;';
29990             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29991                         Math.max(opt.minWidth || this.minWidth, bwidth));
29992             if(opt.prompt){
29993                 activeTextEl.setWidth(w);
29994             }
29995             if(dlg.isVisible()){
29996                 dlg.fixedcenter = false;
29997             }
29998             dlg.setContentSize(w, bodyEl.getHeight());
29999             if(dlg.isVisible()){
30000                 dlg.fixedcenter = true;
30001             }
30002             return this;
30003         },
30004
30005         /**
30006          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30007          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30008          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30009          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30010          * @return {Roo.MessageBox} This message box
30011          */
30012         updateProgress : function(value, text){
30013             if(text){
30014                 this.updateText(text);
30015             }
30016             if (pp) { // weird bug on my firefox - for some reason this is not defined
30017                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30018             }
30019             return this;
30020         },        
30021
30022         /**
30023          * Returns true if the message box is currently displayed
30024          * @return {Boolean} True if the message box is visible, else false
30025          */
30026         isVisible : function(){
30027             return dlg && dlg.isVisible();  
30028         },
30029
30030         /**
30031          * Hides the message box if it is displayed
30032          */
30033         hide : function(){
30034             if(this.isVisible()){
30035                 dlg.hide();
30036             }  
30037         },
30038
30039         /**
30040          * Displays a new message box, or reinitializes an existing message box, based on the config options
30041          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30042          * The following config object properties are supported:
30043          * <pre>
30044 Property    Type             Description
30045 ----------  ---------------  ------------------------------------------------------------------------------------
30046 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30047                                    closes (defaults to undefined)
30048 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30049                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30050 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30051                                    progress and wait dialogs will ignore this property and always hide the
30052                                    close button as they can only be closed programmatically.
30053 cls               String           A custom CSS class to apply to the message box element
30054 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30055                                    displayed (defaults to 75)
30056 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30057                                    function will be btn (the name of the button that was clicked, if applicable,
30058                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30059                                    Progress and wait dialogs will ignore this option since they do not respond to
30060                                    user actions and can only be closed programmatically, so any required function
30061                                    should be called by the same code after it closes the dialog.
30062 icon              String           A CSS class that provides a background image to be used as an icon for
30063                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30064 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30065 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30066 modal             Boolean          False to allow user interaction with the page while the message box is
30067                                    displayed (defaults to true)
30068 msg               String           A string that will replace the existing message box body text (defaults
30069                                    to the XHTML-compliant non-breaking space character '&#160;')
30070 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30071 progress          Boolean          True to display a progress bar (defaults to false)
30072 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30073 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30074 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30075 title             String           The title text
30076 value             String           The string value to set into the active textbox element if displayed
30077 wait              Boolean          True to display a progress bar (defaults to false)
30078 width             Number           The width of the dialog in pixels
30079 </pre>
30080          *
30081          * Example usage:
30082          * <pre><code>
30083 Roo.Msg.show({
30084    title: 'Address',
30085    msg: 'Please enter your address:',
30086    width: 300,
30087    buttons: Roo.MessageBox.OKCANCEL,
30088    multiline: true,
30089    fn: saveAddress,
30090    animEl: 'addAddressBtn'
30091 });
30092 </code></pre>
30093          * @param {Object} config Configuration options
30094          * @return {Roo.MessageBox} This message box
30095          */
30096         show : function(options){
30097             if(this.isVisible()){
30098                 this.hide();
30099             }
30100             var d = this.getDialog();
30101             opt = options;
30102             d.setTitle(opt.title || "&#160;");
30103             d.close.setDisplayed(opt.closable !== false);
30104             activeTextEl = textboxEl;
30105             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30106             if(opt.prompt){
30107                 if(opt.multiline){
30108                     textboxEl.hide();
30109                     textareaEl.show();
30110                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30111                         opt.multiline : this.defaultTextHeight);
30112                     activeTextEl = textareaEl;
30113                 }else{
30114                     textboxEl.show();
30115                     textareaEl.hide();
30116                 }
30117             }else{
30118                 textboxEl.hide();
30119                 textareaEl.hide();
30120             }
30121             progressEl.setDisplayed(opt.progress === true);
30122             this.updateProgress(0);
30123             activeTextEl.dom.value = opt.value || "";
30124             if(opt.prompt){
30125                 dlg.setDefaultButton(activeTextEl);
30126             }else{
30127                 var bs = opt.buttons;
30128                 var db = null;
30129                 if(bs && bs.ok){
30130                     db = buttons["ok"];
30131                 }else if(bs && bs.yes){
30132                     db = buttons["yes"];
30133                 }
30134                 dlg.setDefaultButton(db);
30135             }
30136             bwidth = updateButtons(opt.buttons);
30137             this.updateText(opt.msg);
30138             if(opt.cls){
30139                 d.el.addClass(opt.cls);
30140             }
30141             d.proxyDrag = opt.proxyDrag === true;
30142             d.modal = opt.modal !== false;
30143             d.mask = opt.modal !== false ? mask : false;
30144             if(!d.isVisible()){
30145                 // force it to the end of the z-index stack so it gets a cursor in FF
30146                 document.body.appendChild(dlg.el.dom);
30147                 d.animateTarget = null;
30148                 d.show(options.animEl);
30149             }
30150             return this;
30151         },
30152
30153         /**
30154          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30155          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30156          * and closing the message box when the process is complete.
30157          * @param {String} title The title bar text
30158          * @param {String} msg The message box body text
30159          * @return {Roo.MessageBox} This message box
30160          */
30161         progress : function(title, msg){
30162             this.show({
30163                 title : title,
30164                 msg : msg,
30165                 buttons: false,
30166                 progress:true,
30167                 closable:false,
30168                 minWidth: this.minProgressWidth,
30169                 modal : true
30170             });
30171             return this;
30172         },
30173
30174         /**
30175          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30176          * If a callback function is passed it will be called after the user clicks the button, and the
30177          * id of the button that was clicked will be passed as the only parameter to the callback
30178          * (could also be the top-right close button).
30179          * @param {String} title The title bar text
30180          * @param {String} msg The message box body text
30181          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30182          * @param {Object} scope (optional) The scope of the callback function
30183          * @return {Roo.MessageBox} This message box
30184          */
30185         alert : function(title, msg, fn, scope){
30186             this.show({
30187                 title : title,
30188                 msg : msg,
30189                 buttons: this.OK,
30190                 fn: fn,
30191                 scope : scope,
30192                 modal : true
30193             });
30194             return this;
30195         },
30196
30197         /**
30198          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30199          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30200          * You are responsible for closing the message box when the process is complete.
30201          * @param {String} msg The message box body text
30202          * @param {String} title (optional) The title bar text
30203          * @return {Roo.MessageBox} This message box
30204          */
30205         wait : function(msg, title){
30206             this.show({
30207                 title : title,
30208                 msg : msg,
30209                 buttons: false,
30210                 closable:false,
30211                 progress:true,
30212                 modal:true,
30213                 width:300,
30214                 wait:true
30215             });
30216             waitTimer = Roo.TaskMgr.start({
30217                 run: function(i){
30218                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30219                 },
30220                 interval: 1000
30221             });
30222             return this;
30223         },
30224
30225         /**
30226          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30227          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30228          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30229          * @param {String} title The title bar text
30230          * @param {String} msg The message box body text
30231          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30232          * @param {Object} scope (optional) The scope of the callback function
30233          * @return {Roo.MessageBox} This message box
30234          */
30235         confirm : function(title, msg, fn, scope){
30236             this.show({
30237                 title : title,
30238                 msg : msg,
30239                 buttons: this.YESNO,
30240                 fn: fn,
30241                 scope : scope,
30242                 modal : true
30243             });
30244             return this;
30245         },
30246
30247         /**
30248          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30249          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30250          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30251          * (could also be the top-right close button) and the text that was entered will be passed as the two
30252          * parameters to the callback.
30253          * @param {String} title The title bar text
30254          * @param {String} msg The message box body text
30255          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30256          * @param {Object} scope (optional) The scope of the callback function
30257          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30258          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30259          * @return {Roo.MessageBox} This message box
30260          */
30261         prompt : function(title, msg, fn, scope, multiline){
30262             this.show({
30263                 title : title,
30264                 msg : msg,
30265                 buttons: this.OKCANCEL,
30266                 fn: fn,
30267                 minWidth:250,
30268                 scope : scope,
30269                 prompt:true,
30270                 multiline: multiline,
30271                 modal : true
30272             });
30273             return this;
30274         },
30275
30276         /**
30277          * Button config that displays a single OK button
30278          * @type Object
30279          */
30280         OK : {ok:true},
30281         /**
30282          * Button config that displays Yes and No buttons
30283          * @type Object
30284          */
30285         YESNO : {yes:true, no:true},
30286         /**
30287          * Button config that displays OK and Cancel buttons
30288          * @type Object
30289          */
30290         OKCANCEL : {ok:true, cancel:true},
30291         /**
30292          * Button config that displays Yes, No and Cancel buttons
30293          * @type Object
30294          */
30295         YESNOCANCEL : {yes:true, no:true, cancel:true},
30296
30297         /**
30298          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30299          * @type Number
30300          */
30301         defaultTextHeight : 75,
30302         /**
30303          * The maximum width in pixels of the message box (defaults to 600)
30304          * @type Number
30305          */
30306         maxWidth : 600,
30307         /**
30308          * The minimum width in pixels of the message box (defaults to 100)
30309          * @type Number
30310          */
30311         minWidth : 100,
30312         /**
30313          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30314          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30315          * @type Number
30316          */
30317         minProgressWidth : 250,
30318         /**
30319          * An object containing the default button text strings that can be overriden for localized language support.
30320          * Supported properties are: ok, cancel, yes and no.
30321          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30322          * @type Object
30323          */
30324         buttonText : {
30325             ok : "OK",
30326             cancel : "Cancel",
30327             yes : "Yes",
30328             no : "No"
30329         }
30330     };
30331 }();
30332
30333 /**
30334  * Shorthand for {@link Roo.MessageBox}
30335  */
30336 Roo.Msg = Roo.MessageBox;/*
30337  * Based on:
30338  * Ext JS Library 1.1.1
30339  * Copyright(c) 2006-2007, Ext JS, LLC.
30340  *
30341  * Originally Released Under LGPL - original licence link has changed is not relivant.
30342  *
30343  * Fork - LGPL
30344  * <script type="text/javascript">
30345  */
30346 /**
30347  * @class Roo.QuickTips
30348  * Provides attractive and customizable tooltips for any element.
30349  * @singleton
30350  */
30351 Roo.QuickTips = function(){
30352     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30353     var ce, bd, xy, dd;
30354     var visible = false, disabled = true, inited = false;
30355     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30356     
30357     var onOver = function(e){
30358         if(disabled){
30359             return;
30360         }
30361         var t = e.getTarget();
30362         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30363             return;
30364         }
30365         if(ce && t == ce.el){
30366             clearTimeout(hideProc);
30367             return;
30368         }
30369         if(t && tagEls[t.id]){
30370             tagEls[t.id].el = t;
30371             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30372             return;
30373         }
30374         var ttp, et = Roo.fly(t);
30375         var ns = cfg.namespace;
30376         if(tm.interceptTitles && t.title){
30377             ttp = t.title;
30378             t.qtip = ttp;
30379             t.removeAttribute("title");
30380             e.preventDefault();
30381         }else{
30382             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30383         }
30384         if(ttp){
30385             showProc = show.defer(tm.showDelay, tm, [{
30386                 el: t, 
30387                 text: ttp, 
30388                 width: et.getAttributeNS(ns, cfg.width),
30389                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30390                 title: et.getAttributeNS(ns, cfg.title),
30391                     cls: et.getAttributeNS(ns, cfg.cls)
30392             }]);
30393         }
30394     };
30395     
30396     var onOut = function(e){
30397         clearTimeout(showProc);
30398         var t = e.getTarget();
30399         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30400             hideProc = setTimeout(hide, tm.hideDelay);
30401         }
30402     };
30403     
30404     var onMove = function(e){
30405         if(disabled){
30406             return;
30407         }
30408         xy = e.getXY();
30409         xy[1] += 18;
30410         if(tm.trackMouse && ce){
30411             el.setXY(xy);
30412         }
30413     };
30414     
30415     var onDown = function(e){
30416         clearTimeout(showProc);
30417         clearTimeout(hideProc);
30418         if(!e.within(el)){
30419             if(tm.hideOnClick){
30420                 hide();
30421                 tm.disable();
30422                 tm.enable.defer(100, tm);
30423             }
30424         }
30425     };
30426     
30427     var getPad = function(){
30428         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30429     };
30430
30431     var show = function(o){
30432         if(disabled){
30433             return;
30434         }
30435         clearTimeout(dismissProc);
30436         ce = o;
30437         if(removeCls){ // in case manually hidden
30438             el.removeClass(removeCls);
30439             removeCls = null;
30440         }
30441         if(ce.cls){
30442             el.addClass(ce.cls);
30443             removeCls = ce.cls;
30444         }
30445         if(ce.title){
30446             tipTitle.update(ce.title);
30447             tipTitle.show();
30448         }else{
30449             tipTitle.update('');
30450             tipTitle.hide();
30451         }
30452         el.dom.style.width  = tm.maxWidth+'px';
30453         //tipBody.dom.style.width = '';
30454         tipBodyText.update(o.text);
30455         var p = getPad(), w = ce.width;
30456         if(!w){
30457             var td = tipBodyText.dom;
30458             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30459             if(aw > tm.maxWidth){
30460                 w = tm.maxWidth;
30461             }else if(aw < tm.minWidth){
30462                 w = tm.minWidth;
30463             }else{
30464                 w = aw;
30465             }
30466         }
30467         //tipBody.setWidth(w);
30468         el.setWidth(parseInt(w, 10) + p);
30469         if(ce.autoHide === false){
30470             close.setDisplayed(true);
30471             if(dd){
30472                 dd.unlock();
30473             }
30474         }else{
30475             close.setDisplayed(false);
30476             if(dd){
30477                 dd.lock();
30478             }
30479         }
30480         if(xy){
30481             el.avoidY = xy[1]-18;
30482             el.setXY(xy);
30483         }
30484         if(tm.animate){
30485             el.setOpacity(.1);
30486             el.setStyle("visibility", "visible");
30487             el.fadeIn({callback: afterShow});
30488         }else{
30489             afterShow();
30490         }
30491     };
30492     
30493     var afterShow = function(){
30494         if(ce){
30495             el.show();
30496             esc.enable();
30497             if(tm.autoDismiss && ce.autoHide !== false){
30498                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30499             }
30500         }
30501     };
30502     
30503     var hide = function(noanim){
30504         clearTimeout(dismissProc);
30505         clearTimeout(hideProc);
30506         ce = null;
30507         if(el.isVisible()){
30508             esc.disable();
30509             if(noanim !== true && tm.animate){
30510                 el.fadeOut({callback: afterHide});
30511             }else{
30512                 afterHide();
30513             } 
30514         }
30515     };
30516     
30517     var afterHide = function(){
30518         el.hide();
30519         if(removeCls){
30520             el.removeClass(removeCls);
30521             removeCls = null;
30522         }
30523     };
30524     
30525     return {
30526         /**
30527         * @cfg {Number} minWidth
30528         * The minimum width of the quick tip (defaults to 40)
30529         */
30530        minWidth : 40,
30531         /**
30532         * @cfg {Number} maxWidth
30533         * The maximum width of the quick tip (defaults to 300)
30534         */
30535        maxWidth : 300,
30536         /**
30537         * @cfg {Boolean} interceptTitles
30538         * True to automatically use the element's DOM title value if available (defaults to false)
30539         */
30540        interceptTitles : false,
30541         /**
30542         * @cfg {Boolean} trackMouse
30543         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30544         */
30545        trackMouse : false,
30546         /**
30547         * @cfg {Boolean} hideOnClick
30548         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30549         */
30550        hideOnClick : true,
30551         /**
30552         * @cfg {Number} showDelay
30553         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30554         */
30555        showDelay : 500,
30556         /**
30557         * @cfg {Number} hideDelay
30558         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30559         */
30560        hideDelay : 200,
30561         /**
30562         * @cfg {Boolean} autoHide
30563         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30564         * Used in conjunction with hideDelay.
30565         */
30566        autoHide : true,
30567         /**
30568         * @cfg {Boolean}
30569         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30570         * (defaults to true).  Used in conjunction with autoDismissDelay.
30571         */
30572        autoDismiss : true,
30573         /**
30574         * @cfg {Number}
30575         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30576         */
30577        autoDismissDelay : 5000,
30578        /**
30579         * @cfg {Boolean} animate
30580         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30581         */
30582        animate : false,
30583
30584        /**
30585         * @cfg {String} title
30586         * Title text to display (defaults to '').  This can be any valid HTML markup.
30587         */
30588         title: '',
30589        /**
30590         * @cfg {String} text
30591         * Body text to display (defaults to '').  This can be any valid HTML markup.
30592         */
30593         text : '',
30594        /**
30595         * @cfg {String} cls
30596         * A CSS class to apply to the base quick tip element (defaults to '').
30597         */
30598         cls : '',
30599        /**
30600         * @cfg {Number} width
30601         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30602         * minWidth or maxWidth.
30603         */
30604         width : null,
30605
30606     /**
30607      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30608      * or display QuickTips in a page.
30609      */
30610        init : function(){
30611           tm = Roo.QuickTips;
30612           cfg = tm.tagConfig;
30613           if(!inited){
30614               if(!Roo.isReady){ // allow calling of init() before onReady
30615                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30616                   return;
30617               }
30618               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30619               el.fxDefaults = {stopFx: true};
30620               // maximum custom styling
30621               //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>');
30622               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>');              
30623               tipTitle = el.child('h3');
30624               tipTitle.enableDisplayMode("block");
30625               tipBody = el.child('div.x-tip-bd');
30626               tipBodyText = el.child('div.x-tip-bd-inner');
30627               //bdLeft = el.child('div.x-tip-bd-left');
30628               //bdRight = el.child('div.x-tip-bd-right');
30629               close = el.child('div.x-tip-close');
30630               close.enableDisplayMode("block");
30631               close.on("click", hide);
30632               var d = Roo.get(document);
30633               d.on("mousedown", onDown);
30634               d.on("mouseover", onOver);
30635               d.on("mouseout", onOut);
30636               d.on("mousemove", onMove);
30637               esc = d.addKeyListener(27, hide);
30638               esc.disable();
30639               if(Roo.dd.DD){
30640                   dd = el.initDD("default", null, {
30641                       onDrag : function(){
30642                           el.sync();  
30643                       }
30644                   });
30645                   dd.setHandleElId(tipTitle.id);
30646                   dd.lock();
30647               }
30648               inited = true;
30649           }
30650           this.enable(); 
30651        },
30652
30653     /**
30654      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30655      * are supported:
30656      * <pre>
30657 Property    Type                   Description
30658 ----------  ---------------------  ------------------------------------------------------------------------
30659 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30660      * </ul>
30661      * @param {Object} config The config object
30662      */
30663        register : function(config){
30664            var cs = config instanceof Array ? config : arguments;
30665            for(var i = 0, len = cs.length; i < len; i++) {
30666                var c = cs[i];
30667                var target = c.target;
30668                if(target){
30669                    if(target instanceof Array){
30670                        for(var j = 0, jlen = target.length; j < jlen; j++){
30671                            tagEls[target[j]] = c;
30672                        }
30673                    }else{
30674                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30675                    }
30676                }
30677            }
30678        },
30679
30680     /**
30681      * Removes this quick tip from its element and destroys it.
30682      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30683      */
30684        unregister : function(el){
30685            delete tagEls[Roo.id(el)];
30686        },
30687
30688     /**
30689      * Enable this quick tip.
30690      */
30691        enable : function(){
30692            if(inited && disabled){
30693                locks.pop();
30694                if(locks.length < 1){
30695                    disabled = false;
30696                }
30697            }
30698        },
30699
30700     /**
30701      * Disable this quick tip.
30702      */
30703        disable : function(){
30704           disabled = true;
30705           clearTimeout(showProc);
30706           clearTimeout(hideProc);
30707           clearTimeout(dismissProc);
30708           if(ce){
30709               hide(true);
30710           }
30711           locks.push(1);
30712        },
30713
30714     /**
30715      * Returns true if the quick tip is enabled, else false.
30716      */
30717        isEnabled : function(){
30718             return !disabled;
30719        },
30720
30721         // private
30722        tagConfig : {
30723            namespace : "ext",
30724            attribute : "qtip",
30725            width : "width",
30726            target : "target",
30727            title : "qtitle",
30728            hide : "hide",
30729            cls : "qclass"
30730        }
30731    };
30732 }();
30733
30734 // backwards compat
30735 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30736  * Based on:
30737  * Ext JS Library 1.1.1
30738  * Copyright(c) 2006-2007, Ext JS, LLC.
30739  *
30740  * Originally Released Under LGPL - original licence link has changed is not relivant.
30741  *
30742  * Fork - LGPL
30743  * <script type="text/javascript">
30744  */
30745  
30746
30747 /**
30748  * @class Roo.tree.TreePanel
30749  * @extends Roo.data.Tree
30750
30751  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30752  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30753  * @cfg {Boolean} enableDD true to enable drag and drop
30754  * @cfg {Boolean} enableDrag true to enable just drag
30755  * @cfg {Boolean} enableDrop true to enable just drop
30756  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30757  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30758  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30759  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30760  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30761  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30762  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30763  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30764  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30765  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30766  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30767  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30768  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30769  * @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>
30770  * @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>
30771  * 
30772  * @constructor
30773  * @param {String/HTMLElement/Element} el The container element
30774  * @param {Object} config
30775  */
30776 Roo.tree.TreePanel = function(el, config){
30777     var root = false;
30778     var loader = false;
30779     if (config.root) {
30780         root = config.root;
30781         delete config.root;
30782     }
30783     if (config.loader) {
30784         loader = config.loader;
30785         delete config.loader;
30786     }
30787     
30788     Roo.apply(this, config);
30789     Roo.tree.TreePanel.superclass.constructor.call(this);
30790     this.el = Roo.get(el);
30791     this.el.addClass('x-tree');
30792     //console.log(root);
30793     if (root) {
30794         this.setRootNode( Roo.factory(root, Roo.tree));
30795     }
30796     if (loader) {
30797         this.loader = Roo.factory(loader, Roo.tree);
30798     }
30799    /**
30800     * Read-only. The id of the container element becomes this TreePanel's id.
30801     */
30802    this.id = this.el.id;
30803    this.addEvents({
30804         /**
30805         * @event beforeload
30806         * Fires before a node is loaded, return false to cancel
30807         * @param {Node} node The node being loaded
30808         */
30809         "beforeload" : true,
30810         /**
30811         * @event load
30812         * Fires when a node is loaded
30813         * @param {Node} node The node that was loaded
30814         */
30815         "load" : true,
30816         /**
30817         * @event textchange
30818         * Fires when the text for a node is changed
30819         * @param {Node} node The node
30820         * @param {String} text The new text
30821         * @param {String} oldText The old text
30822         */
30823         "textchange" : true,
30824         /**
30825         * @event beforeexpand
30826         * Fires before a node is expanded, return false to cancel.
30827         * @param {Node} node The node
30828         * @param {Boolean} deep
30829         * @param {Boolean} anim
30830         */
30831         "beforeexpand" : true,
30832         /**
30833         * @event beforecollapse
30834         * Fires before a node is collapsed, return false to cancel.
30835         * @param {Node} node The node
30836         * @param {Boolean} deep
30837         * @param {Boolean} anim
30838         */
30839         "beforecollapse" : true,
30840         /**
30841         * @event expand
30842         * Fires when a node is expanded
30843         * @param {Node} node The node
30844         */
30845         "expand" : true,
30846         /**
30847         * @event disabledchange
30848         * Fires when the disabled status of a node changes
30849         * @param {Node} node The node
30850         * @param {Boolean} disabled
30851         */
30852         "disabledchange" : true,
30853         /**
30854         * @event collapse
30855         * Fires when a node is collapsed
30856         * @param {Node} node The node
30857         */
30858         "collapse" : true,
30859         /**
30860         * @event beforeclick
30861         * Fires before click processing on a node. Return false to cancel the default action.
30862         * @param {Node} node The node
30863         * @param {Roo.EventObject} e The event object
30864         */
30865         "beforeclick":true,
30866         /**
30867         * @event checkchange
30868         * Fires when a node with a checkbox's checked property changes
30869         * @param {Node} this This node
30870         * @param {Boolean} checked
30871         */
30872         "checkchange":true,
30873         /**
30874         * @event click
30875         * Fires when a node is clicked
30876         * @param {Node} node The node
30877         * @param {Roo.EventObject} e The event object
30878         */
30879         "click":true,
30880         /**
30881         * @event dblclick
30882         * Fires when a node is double clicked
30883         * @param {Node} node The node
30884         * @param {Roo.EventObject} e The event object
30885         */
30886         "dblclick":true,
30887         /**
30888         * @event contextmenu
30889         * Fires when a node is right clicked
30890         * @param {Node} node The node
30891         * @param {Roo.EventObject} e The event object
30892         */
30893         "contextmenu":true,
30894         /**
30895         * @event beforechildrenrendered
30896         * Fires right before the child nodes for a node are rendered
30897         * @param {Node} node The node
30898         */
30899         "beforechildrenrendered":true,
30900        /**
30901              * @event startdrag
30902              * Fires when a node starts being dragged
30903              * @param {Roo.tree.TreePanel} this
30904              * @param {Roo.tree.TreeNode} node
30905              * @param {event} e The raw browser event
30906              */ 
30907             "startdrag" : true,
30908             /**
30909              * @event enddrag
30910              * Fires when a drag operation is complete
30911              * @param {Roo.tree.TreePanel} this
30912              * @param {Roo.tree.TreeNode} node
30913              * @param {event} e The raw browser event
30914              */
30915             "enddrag" : true,
30916             /**
30917              * @event dragdrop
30918              * Fires when a dragged node is dropped on a valid DD target
30919              * @param {Roo.tree.TreePanel} this
30920              * @param {Roo.tree.TreeNode} node
30921              * @param {DD} dd The dd it was dropped on
30922              * @param {event} e The raw browser event
30923              */
30924             "dragdrop" : true,
30925             /**
30926              * @event beforenodedrop
30927              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30928              * passed to handlers has the following properties:<br />
30929              * <ul style="padding:5px;padding-left:16px;">
30930              * <li>tree - The TreePanel</li>
30931              * <li>target - The node being targeted for the drop</li>
30932              * <li>data - The drag data from the drag source</li>
30933              * <li>point - The point of the drop - append, above or below</li>
30934              * <li>source - The drag source</li>
30935              * <li>rawEvent - Raw mouse event</li>
30936              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30937              * to be inserted by setting them on this object.</li>
30938              * <li>cancel - Set this to true to cancel the drop.</li>
30939              * </ul>
30940              * @param {Object} dropEvent
30941              */
30942             "beforenodedrop" : true,
30943             /**
30944              * @event nodedrop
30945              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30946              * passed to handlers has the following properties:<br />
30947              * <ul style="padding:5px;padding-left:16px;">
30948              * <li>tree - The TreePanel</li>
30949              * <li>target - The node being targeted for the drop</li>
30950              * <li>data - The drag data from the drag source</li>
30951              * <li>point - The point of the drop - append, above or below</li>
30952              * <li>source - The drag source</li>
30953              * <li>rawEvent - Raw mouse event</li>
30954              * <li>dropNode - Dropped node(s).</li>
30955              * </ul>
30956              * @param {Object} dropEvent
30957              */
30958             "nodedrop" : true,
30959              /**
30960              * @event nodedragover
30961              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30962              * passed to handlers has the following properties:<br />
30963              * <ul style="padding:5px;padding-left:16px;">
30964              * <li>tree - The TreePanel</li>
30965              * <li>target - The node being targeted for the drop</li>
30966              * <li>data - The drag data from the drag source</li>
30967              * <li>point - The point of the drop - append, above or below</li>
30968              * <li>source - The drag source</li>
30969              * <li>rawEvent - Raw mouse event</li>
30970              * <li>dropNode - Drop node(s) provided by the source.</li>
30971              * <li>cancel - Set this to true to signal drop not allowed.</li>
30972              * </ul>
30973              * @param {Object} dragOverEvent
30974              */
30975             "nodedragover" : true
30976         
30977    });
30978    if(this.singleExpand){
30979        this.on("beforeexpand", this.restrictExpand, this);
30980    }
30981 };
30982 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30983     rootVisible : true,
30984     animate: Roo.enableFx,
30985     lines : true,
30986     enableDD : false,
30987     hlDrop : Roo.enableFx,
30988   
30989     renderer: false,
30990     
30991     rendererTip: false,
30992     // private
30993     restrictExpand : function(node){
30994         var p = node.parentNode;
30995         if(p){
30996             if(p.expandedChild && p.expandedChild.parentNode == p){
30997                 p.expandedChild.collapse();
30998             }
30999             p.expandedChild = node;
31000         }
31001     },
31002
31003     // private override
31004     setRootNode : function(node){
31005         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31006         if(!this.rootVisible){
31007             node.ui = new Roo.tree.RootTreeNodeUI(node);
31008         }
31009         return node;
31010     },
31011
31012     /**
31013      * Returns the container element for this TreePanel
31014      */
31015     getEl : function(){
31016         return this.el;
31017     },
31018
31019     /**
31020      * Returns the default TreeLoader for this TreePanel
31021      */
31022     getLoader : function(){
31023         return this.loader;
31024     },
31025
31026     /**
31027      * Expand all nodes
31028      */
31029     expandAll : function(){
31030         this.root.expand(true);
31031     },
31032
31033     /**
31034      * Collapse all nodes
31035      */
31036     collapseAll : function(){
31037         this.root.collapse(true);
31038     },
31039
31040     /**
31041      * Returns the selection model used by this TreePanel
31042      */
31043     getSelectionModel : function(){
31044         if(!this.selModel){
31045             this.selModel = new Roo.tree.DefaultSelectionModel();
31046         }
31047         return this.selModel;
31048     },
31049
31050     /**
31051      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31052      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31053      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31054      * @return {Array}
31055      */
31056     getChecked : function(a, startNode){
31057         startNode = startNode || this.root;
31058         var r = [];
31059         var f = function(){
31060             if(this.attributes.checked){
31061                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31062             }
31063         }
31064         startNode.cascade(f);
31065         return r;
31066     },
31067
31068     /**
31069      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31070      * @param {String} path
31071      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31072      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31073      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31074      */
31075     expandPath : function(path, attr, callback){
31076         attr = attr || "id";
31077         var keys = path.split(this.pathSeparator);
31078         var curNode = this.root;
31079         if(curNode.attributes[attr] != keys[1]){ // invalid root
31080             if(callback){
31081                 callback(false, null);
31082             }
31083             return;
31084         }
31085         var index = 1;
31086         var f = function(){
31087             if(++index == keys.length){
31088                 if(callback){
31089                     callback(true, curNode);
31090                 }
31091                 return;
31092             }
31093             var c = curNode.findChild(attr, keys[index]);
31094             if(!c){
31095                 if(callback){
31096                     callback(false, curNode);
31097                 }
31098                 return;
31099             }
31100             curNode = c;
31101             c.expand(false, false, f);
31102         };
31103         curNode.expand(false, false, f);
31104     },
31105
31106     /**
31107      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31108      * @param {String} path
31109      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31110      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31111      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31112      */
31113     selectPath : function(path, attr, callback){
31114         attr = attr || "id";
31115         var keys = path.split(this.pathSeparator);
31116         var v = keys.pop();
31117         if(keys.length > 0){
31118             var f = function(success, node){
31119                 if(success && node){
31120                     var n = node.findChild(attr, v);
31121                     if(n){
31122                         n.select();
31123                         if(callback){
31124                             callback(true, n);
31125                         }
31126                     }else if(callback){
31127                         callback(false, n);
31128                     }
31129                 }else{
31130                     if(callback){
31131                         callback(false, n);
31132                     }
31133                 }
31134             };
31135             this.expandPath(keys.join(this.pathSeparator), attr, f);
31136         }else{
31137             this.root.select();
31138             if(callback){
31139                 callback(true, this.root);
31140             }
31141         }
31142     },
31143
31144     getTreeEl : function(){
31145         return this.el;
31146     },
31147
31148     /**
31149      * Trigger rendering of this TreePanel
31150      */
31151     render : function(){
31152         if (this.innerCt) {
31153             return this; // stop it rendering more than once!!
31154         }
31155         
31156         this.innerCt = this.el.createChild({tag:"ul",
31157                cls:"x-tree-root-ct " +
31158                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31159
31160         if(this.containerScroll){
31161             Roo.dd.ScrollManager.register(this.el);
31162         }
31163         if((this.enableDD || this.enableDrop) && !this.dropZone){
31164            /**
31165             * The dropZone used by this tree if drop is enabled
31166             * @type Roo.tree.TreeDropZone
31167             */
31168              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31169                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31170            });
31171         }
31172         if((this.enableDD || this.enableDrag) && !this.dragZone){
31173            /**
31174             * The dragZone used by this tree if drag is enabled
31175             * @type Roo.tree.TreeDragZone
31176             */
31177             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31178                ddGroup: this.ddGroup || "TreeDD",
31179                scroll: this.ddScroll
31180            });
31181         }
31182         this.getSelectionModel().init(this);
31183         if (!this.root) {
31184             console.log("ROOT not set in tree");
31185             return;
31186         }
31187         this.root.render();
31188         if(!this.rootVisible){
31189             this.root.renderChildren();
31190         }
31191         return this;
31192     }
31193 });/*
31194  * Based on:
31195  * Ext JS Library 1.1.1
31196  * Copyright(c) 2006-2007, Ext JS, LLC.
31197  *
31198  * Originally Released Under LGPL - original licence link has changed is not relivant.
31199  *
31200  * Fork - LGPL
31201  * <script type="text/javascript">
31202  */
31203  
31204
31205 /**
31206  * @class Roo.tree.DefaultSelectionModel
31207  * @extends Roo.util.Observable
31208  * The default single selection for a TreePanel.
31209  */
31210 Roo.tree.DefaultSelectionModel = function(){
31211    this.selNode = null;
31212    
31213    this.addEvents({
31214        /**
31215         * @event selectionchange
31216         * Fires when the selected node changes
31217         * @param {DefaultSelectionModel} this
31218         * @param {TreeNode} node the new selection
31219         */
31220        "selectionchange" : true,
31221
31222        /**
31223         * @event beforeselect
31224         * Fires before the selected node changes, return false to cancel the change
31225         * @param {DefaultSelectionModel} this
31226         * @param {TreeNode} node the new selection
31227         * @param {TreeNode} node the old selection
31228         */
31229        "beforeselect" : true
31230    });
31231 };
31232
31233 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31234     init : function(tree){
31235         this.tree = tree;
31236         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31237         tree.on("click", this.onNodeClick, this);
31238     },
31239     
31240     onNodeClick : function(node, e){
31241         if (e.ctrlKey && this.selNode == node)  {
31242             this.unselect(node);
31243             return;
31244         }
31245         this.select(node);
31246     },
31247     
31248     /**
31249      * Select a node.
31250      * @param {TreeNode} node The node to select
31251      * @return {TreeNode} The selected node
31252      */
31253     select : function(node){
31254         var last = this.selNode;
31255         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31256             if(last){
31257                 last.ui.onSelectedChange(false);
31258             }
31259             this.selNode = node;
31260             node.ui.onSelectedChange(true);
31261             this.fireEvent("selectionchange", this, node, last);
31262         }
31263         return node;
31264     },
31265     
31266     /**
31267      * Deselect a node.
31268      * @param {TreeNode} node The node to unselect
31269      */
31270     unselect : function(node){
31271         if(this.selNode == node){
31272             this.clearSelections();
31273         }    
31274     },
31275     
31276     /**
31277      * Clear all selections
31278      */
31279     clearSelections : function(){
31280         var n = this.selNode;
31281         if(n){
31282             n.ui.onSelectedChange(false);
31283             this.selNode = null;
31284             this.fireEvent("selectionchange", this, null);
31285         }
31286         return n;
31287     },
31288     
31289     /**
31290      * Get the selected node
31291      * @return {TreeNode} The selected node
31292      */
31293     getSelectedNode : function(){
31294         return this.selNode;    
31295     },
31296     
31297     /**
31298      * Returns true if the node is selected
31299      * @param {TreeNode} node The node to check
31300      * @return {Boolean}
31301      */
31302     isSelected : function(node){
31303         return this.selNode == node;  
31304     },
31305
31306     /**
31307      * Selects the node above the selected node in the tree, intelligently walking the nodes
31308      * @return TreeNode The new selection
31309      */
31310     selectPrevious : function(){
31311         var s = this.selNode || this.lastSelNode;
31312         if(!s){
31313             return null;
31314         }
31315         var ps = s.previousSibling;
31316         if(ps){
31317             if(!ps.isExpanded() || ps.childNodes.length < 1){
31318                 return this.select(ps);
31319             } else{
31320                 var lc = ps.lastChild;
31321                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31322                     lc = lc.lastChild;
31323                 }
31324                 return this.select(lc);
31325             }
31326         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31327             return this.select(s.parentNode);
31328         }
31329         return null;
31330     },
31331
31332     /**
31333      * Selects the node above the selected node in the tree, intelligently walking the nodes
31334      * @return TreeNode The new selection
31335      */
31336     selectNext : function(){
31337         var s = this.selNode || this.lastSelNode;
31338         if(!s){
31339             return null;
31340         }
31341         if(s.firstChild && s.isExpanded()){
31342              return this.select(s.firstChild);
31343          }else if(s.nextSibling){
31344              return this.select(s.nextSibling);
31345          }else if(s.parentNode){
31346             var newS = null;
31347             s.parentNode.bubble(function(){
31348                 if(this.nextSibling){
31349                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31350                     return false;
31351                 }
31352             });
31353             return newS;
31354          }
31355         return null;
31356     },
31357
31358     onKeyDown : function(e){
31359         var s = this.selNode || this.lastSelNode;
31360         // undesirable, but required
31361         var sm = this;
31362         if(!s){
31363             return;
31364         }
31365         var k = e.getKey();
31366         switch(k){
31367              case e.DOWN:
31368                  e.stopEvent();
31369                  this.selectNext();
31370              break;
31371              case e.UP:
31372                  e.stopEvent();
31373                  this.selectPrevious();
31374              break;
31375              case e.RIGHT:
31376                  e.preventDefault();
31377                  if(s.hasChildNodes()){
31378                      if(!s.isExpanded()){
31379                          s.expand();
31380                      }else if(s.firstChild){
31381                          this.select(s.firstChild, e);
31382                      }
31383                  }
31384              break;
31385              case e.LEFT:
31386                  e.preventDefault();
31387                  if(s.hasChildNodes() && s.isExpanded()){
31388                      s.collapse();
31389                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31390                      this.select(s.parentNode, e);
31391                  }
31392              break;
31393         };
31394     }
31395 });
31396
31397 /**
31398  * @class Roo.tree.MultiSelectionModel
31399  * @extends Roo.util.Observable
31400  * Multi selection for a TreePanel.
31401  */
31402 Roo.tree.MultiSelectionModel = function(){
31403    this.selNodes = [];
31404    this.selMap = {};
31405    this.addEvents({
31406        /**
31407         * @event selectionchange
31408         * Fires when the selected nodes change
31409         * @param {MultiSelectionModel} this
31410         * @param {Array} nodes Array of the selected nodes
31411         */
31412        "selectionchange" : true
31413    });
31414 };
31415
31416 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31417     init : function(tree){
31418         this.tree = tree;
31419         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31420         tree.on("click", this.onNodeClick, this);
31421     },
31422     
31423     onNodeClick : function(node, e){
31424         this.select(node, e, e.ctrlKey);
31425     },
31426     
31427     /**
31428      * Select a node.
31429      * @param {TreeNode} node The node to select
31430      * @param {EventObject} e (optional) An event associated with the selection
31431      * @param {Boolean} keepExisting True to retain existing selections
31432      * @return {TreeNode} The selected node
31433      */
31434     select : function(node, e, keepExisting){
31435         if(keepExisting !== true){
31436             this.clearSelections(true);
31437         }
31438         if(this.isSelected(node)){
31439             this.lastSelNode = node;
31440             return node;
31441         }
31442         this.selNodes.push(node);
31443         this.selMap[node.id] = node;
31444         this.lastSelNode = node;
31445         node.ui.onSelectedChange(true);
31446         this.fireEvent("selectionchange", this, this.selNodes);
31447         return node;
31448     },
31449     
31450     /**
31451      * Deselect a node.
31452      * @param {TreeNode} node The node to unselect
31453      */
31454     unselect : function(node){
31455         if(this.selMap[node.id]){
31456             node.ui.onSelectedChange(false);
31457             var sn = this.selNodes;
31458             var index = -1;
31459             if(sn.indexOf){
31460                 index = sn.indexOf(node);
31461             }else{
31462                 for(var i = 0, len = sn.length; i < len; i++){
31463                     if(sn[i] == node){
31464                         index = i;
31465                         break;
31466                     }
31467                 }
31468             }
31469             if(index != -1){
31470                 this.selNodes.splice(index, 1);
31471             }
31472             delete this.selMap[node.id];
31473             this.fireEvent("selectionchange", this, this.selNodes);
31474         }
31475     },
31476     
31477     /**
31478      * Clear all selections
31479      */
31480     clearSelections : function(suppressEvent){
31481         var sn = this.selNodes;
31482         if(sn.length > 0){
31483             for(var i = 0, len = sn.length; i < len; i++){
31484                 sn[i].ui.onSelectedChange(false);
31485             }
31486             this.selNodes = [];
31487             this.selMap = {};
31488             if(suppressEvent !== true){
31489                 this.fireEvent("selectionchange", this, this.selNodes);
31490             }
31491         }
31492     },
31493     
31494     /**
31495      * Returns true if the node is selected
31496      * @param {TreeNode} node The node to check
31497      * @return {Boolean}
31498      */
31499     isSelected : function(node){
31500         return this.selMap[node.id] ? true : false;  
31501     },
31502     
31503     /**
31504      * Returns an array of the selected nodes
31505      * @return {Array}
31506      */
31507     getSelectedNodes : function(){
31508         return this.selNodes;    
31509     },
31510
31511     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31512
31513     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31514
31515     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31516 });/*
31517  * Based on:
31518  * Ext JS Library 1.1.1
31519  * Copyright(c) 2006-2007, Ext JS, LLC.
31520  *
31521  * Originally Released Under LGPL - original licence link has changed is not relivant.
31522  *
31523  * Fork - LGPL
31524  * <script type="text/javascript">
31525  */
31526  
31527 /**
31528  * @class Roo.tree.TreeNode
31529  * @extends Roo.data.Node
31530  * @cfg {String} text The text for this node
31531  * @cfg {Boolean} expanded true to start the node expanded
31532  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31533  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31534  * @cfg {Boolean} disabled true to start the node disabled
31535  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31536  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31537  * @cfg {String} cls A css class to be added to the node
31538  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31539  * @cfg {String} href URL of the link used for the node (defaults to #)
31540  * @cfg {String} hrefTarget target frame for the link
31541  * @cfg {String} qtip An Ext QuickTip for the node
31542  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31543  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31544  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31545  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31546  * (defaults to undefined with no checkbox rendered)
31547  * @constructor
31548  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31549  */
31550 Roo.tree.TreeNode = function(attributes){
31551     attributes = attributes || {};
31552     if(typeof attributes == "string"){
31553         attributes = {text: attributes};
31554     }
31555     this.childrenRendered = false;
31556     this.rendered = false;
31557     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31558     this.expanded = attributes.expanded === true;
31559     this.isTarget = attributes.isTarget !== false;
31560     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31561     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31562
31563     /**
31564      * Read-only. The text for this node. To change it use setText().
31565      * @type String
31566      */
31567     this.text = attributes.text;
31568     /**
31569      * True if this node is disabled.
31570      * @type Boolean
31571      */
31572     this.disabled = attributes.disabled === true;
31573
31574     this.addEvents({
31575         /**
31576         * @event textchange
31577         * Fires when the text for this node is changed
31578         * @param {Node} this This node
31579         * @param {String} text The new text
31580         * @param {String} oldText The old text
31581         */
31582         "textchange" : true,
31583         /**
31584         * @event beforeexpand
31585         * Fires before this node is expanded, return false to cancel.
31586         * @param {Node} this This node
31587         * @param {Boolean} deep
31588         * @param {Boolean} anim
31589         */
31590         "beforeexpand" : true,
31591         /**
31592         * @event beforecollapse
31593         * Fires before this node is collapsed, return false to cancel.
31594         * @param {Node} this This node
31595         * @param {Boolean} deep
31596         * @param {Boolean} anim
31597         */
31598         "beforecollapse" : true,
31599         /**
31600         * @event expand
31601         * Fires when this node is expanded
31602         * @param {Node} this This node
31603         */
31604         "expand" : true,
31605         /**
31606         * @event disabledchange
31607         * Fires when the disabled status of this node changes
31608         * @param {Node} this This node
31609         * @param {Boolean} disabled
31610         */
31611         "disabledchange" : true,
31612         /**
31613         * @event collapse
31614         * Fires when this node is collapsed
31615         * @param {Node} this This node
31616         */
31617         "collapse" : true,
31618         /**
31619         * @event beforeclick
31620         * Fires before click processing. Return false to cancel the default action.
31621         * @param {Node} this This node
31622         * @param {Roo.EventObject} e The event object
31623         */
31624         "beforeclick":true,
31625         /**
31626         * @event checkchange
31627         * Fires when a node with a checkbox's checked property changes
31628         * @param {Node} this This node
31629         * @param {Boolean} checked
31630         */
31631         "checkchange":true,
31632         /**
31633         * @event click
31634         * Fires when this node is clicked
31635         * @param {Node} this This node
31636         * @param {Roo.EventObject} e The event object
31637         */
31638         "click":true,
31639         /**
31640         * @event dblclick
31641         * Fires when this node is double clicked
31642         * @param {Node} this This node
31643         * @param {Roo.EventObject} e The event object
31644         */
31645         "dblclick":true,
31646         /**
31647         * @event contextmenu
31648         * Fires when this node is right clicked
31649         * @param {Node} this This node
31650         * @param {Roo.EventObject} e The event object
31651         */
31652         "contextmenu":true,
31653         /**
31654         * @event beforechildrenrendered
31655         * Fires right before the child nodes for this node are rendered
31656         * @param {Node} this This node
31657         */
31658         "beforechildrenrendered":true
31659     });
31660
31661     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31662
31663     /**
31664      * Read-only. The UI for this node
31665      * @type TreeNodeUI
31666      */
31667     this.ui = new uiClass(this);
31668 };
31669 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31670     preventHScroll: true,
31671     /**
31672      * Returns true if this node is expanded
31673      * @return {Boolean}
31674      */
31675     isExpanded : function(){
31676         return this.expanded;
31677     },
31678
31679     /**
31680      * Returns the UI object for this node
31681      * @return {TreeNodeUI}
31682      */
31683     getUI : function(){
31684         return this.ui;
31685     },
31686
31687     // private override
31688     setFirstChild : function(node){
31689         var of = this.firstChild;
31690         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31691         if(this.childrenRendered && of && node != of){
31692             of.renderIndent(true, true);
31693         }
31694         if(this.rendered){
31695             this.renderIndent(true, true);
31696         }
31697     },
31698
31699     // private override
31700     setLastChild : function(node){
31701         var ol = this.lastChild;
31702         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31703         if(this.childrenRendered && ol && node != ol){
31704             ol.renderIndent(true, true);
31705         }
31706         if(this.rendered){
31707             this.renderIndent(true, true);
31708         }
31709     },
31710
31711     // these methods are overridden to provide lazy rendering support
31712     // private override
31713     appendChild : function(){
31714         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31715         if(node && this.childrenRendered){
31716             node.render();
31717         }
31718         this.ui.updateExpandIcon();
31719         return node;
31720     },
31721
31722     // private override
31723     removeChild : function(node){
31724         this.ownerTree.getSelectionModel().unselect(node);
31725         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31726         // if it's been rendered remove dom node
31727         if(this.childrenRendered){
31728             node.ui.remove();
31729         }
31730         if(this.childNodes.length < 1){
31731             this.collapse(false, false);
31732         }else{
31733             this.ui.updateExpandIcon();
31734         }
31735         if(!this.firstChild) {
31736             this.childrenRendered = false;
31737         }
31738         return node;
31739     },
31740
31741     // private override
31742     insertBefore : function(node, refNode){
31743         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31744         if(newNode && refNode && this.childrenRendered){
31745             node.render();
31746         }
31747         this.ui.updateExpandIcon();
31748         return newNode;
31749     },
31750
31751     /**
31752      * Sets the text for this node
31753      * @param {String} text
31754      */
31755     setText : function(text){
31756         var oldText = this.text;
31757         this.text = text;
31758         this.attributes.text = text;
31759         if(this.rendered){ // event without subscribing
31760             this.ui.onTextChange(this, text, oldText);
31761         }
31762         this.fireEvent("textchange", this, text, oldText);
31763     },
31764
31765     /**
31766      * Triggers selection of this node
31767      */
31768     select : function(){
31769         this.getOwnerTree().getSelectionModel().select(this);
31770     },
31771
31772     /**
31773      * Triggers deselection of this node
31774      */
31775     unselect : function(){
31776         this.getOwnerTree().getSelectionModel().unselect(this);
31777     },
31778
31779     /**
31780      * Returns true if this node is selected
31781      * @return {Boolean}
31782      */
31783     isSelected : function(){
31784         return this.getOwnerTree().getSelectionModel().isSelected(this);
31785     },
31786
31787     /**
31788      * Expand this node.
31789      * @param {Boolean} deep (optional) True to expand all children as well
31790      * @param {Boolean} anim (optional) false to cancel the default animation
31791      * @param {Function} callback (optional) A callback to be called when
31792      * expanding this node completes (does not wait for deep expand to complete).
31793      * Called with 1 parameter, this node.
31794      */
31795     expand : function(deep, anim, callback){
31796         if(!this.expanded){
31797             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31798                 return;
31799             }
31800             if(!this.childrenRendered){
31801                 this.renderChildren();
31802             }
31803             this.expanded = true;
31804             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31805                 this.ui.animExpand(function(){
31806                     this.fireEvent("expand", this);
31807                     if(typeof callback == "function"){
31808                         callback(this);
31809                     }
31810                     if(deep === true){
31811                         this.expandChildNodes(true);
31812                     }
31813                 }.createDelegate(this));
31814                 return;
31815             }else{
31816                 this.ui.expand();
31817                 this.fireEvent("expand", this);
31818                 if(typeof callback == "function"){
31819                     callback(this);
31820                 }
31821             }
31822         }else{
31823            if(typeof callback == "function"){
31824                callback(this);
31825            }
31826         }
31827         if(deep === true){
31828             this.expandChildNodes(true);
31829         }
31830     },
31831
31832     isHiddenRoot : function(){
31833         return this.isRoot && !this.getOwnerTree().rootVisible;
31834     },
31835
31836     /**
31837      * Collapse this node.
31838      * @param {Boolean} deep (optional) True to collapse all children as well
31839      * @param {Boolean} anim (optional) false to cancel the default animation
31840      */
31841     collapse : function(deep, anim){
31842         if(this.expanded && !this.isHiddenRoot()){
31843             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31844                 return;
31845             }
31846             this.expanded = false;
31847             if((this.getOwnerTree().animate && anim !== false) || anim){
31848                 this.ui.animCollapse(function(){
31849                     this.fireEvent("collapse", this);
31850                     if(deep === true){
31851                         this.collapseChildNodes(true);
31852                     }
31853                 }.createDelegate(this));
31854                 return;
31855             }else{
31856                 this.ui.collapse();
31857                 this.fireEvent("collapse", this);
31858             }
31859         }
31860         if(deep === true){
31861             var cs = this.childNodes;
31862             for(var i = 0, len = cs.length; i < len; i++) {
31863                 cs[i].collapse(true, false);
31864             }
31865         }
31866     },
31867
31868     // private
31869     delayedExpand : function(delay){
31870         if(!this.expandProcId){
31871             this.expandProcId = this.expand.defer(delay, this);
31872         }
31873     },
31874
31875     // private
31876     cancelExpand : function(){
31877         if(this.expandProcId){
31878             clearTimeout(this.expandProcId);
31879         }
31880         this.expandProcId = false;
31881     },
31882
31883     /**
31884      * Toggles expanded/collapsed state of the node
31885      */
31886     toggle : function(){
31887         if(this.expanded){
31888             this.collapse();
31889         }else{
31890             this.expand();
31891         }
31892     },
31893
31894     /**
31895      * Ensures all parent nodes are expanded
31896      */
31897     ensureVisible : function(callback){
31898         var tree = this.getOwnerTree();
31899         tree.expandPath(this.parentNode.getPath(), false, function(){
31900             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31901             Roo.callback(callback);
31902         }.createDelegate(this));
31903     },
31904
31905     /**
31906      * Expand all child nodes
31907      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31908      */
31909     expandChildNodes : function(deep){
31910         var cs = this.childNodes;
31911         for(var i = 0, len = cs.length; i < len; i++) {
31912                 cs[i].expand(deep);
31913         }
31914     },
31915
31916     /**
31917      * Collapse all child nodes
31918      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31919      */
31920     collapseChildNodes : function(deep){
31921         var cs = this.childNodes;
31922         for(var i = 0, len = cs.length; i < len; i++) {
31923                 cs[i].collapse(deep);
31924         }
31925     },
31926
31927     /**
31928      * Disables this node
31929      */
31930     disable : function(){
31931         this.disabled = true;
31932         this.unselect();
31933         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31934             this.ui.onDisableChange(this, true);
31935         }
31936         this.fireEvent("disabledchange", this, true);
31937     },
31938
31939     /**
31940      * Enables this node
31941      */
31942     enable : function(){
31943         this.disabled = false;
31944         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31945             this.ui.onDisableChange(this, false);
31946         }
31947         this.fireEvent("disabledchange", this, false);
31948     },
31949
31950     // private
31951     renderChildren : function(suppressEvent){
31952         if(suppressEvent !== false){
31953             this.fireEvent("beforechildrenrendered", this);
31954         }
31955         var cs = this.childNodes;
31956         for(var i = 0, len = cs.length; i < len; i++){
31957             cs[i].render(true);
31958         }
31959         this.childrenRendered = true;
31960     },
31961
31962     // private
31963     sort : function(fn, scope){
31964         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31965         if(this.childrenRendered){
31966             var cs = this.childNodes;
31967             for(var i = 0, len = cs.length; i < len; i++){
31968                 cs[i].render(true);
31969             }
31970         }
31971     },
31972
31973     // private
31974     render : function(bulkRender){
31975         this.ui.render(bulkRender);
31976         if(!this.rendered){
31977             this.rendered = true;
31978             if(this.expanded){
31979                 this.expanded = false;
31980                 this.expand(false, false);
31981             }
31982         }
31983     },
31984
31985     // private
31986     renderIndent : function(deep, refresh){
31987         if(refresh){
31988             this.ui.childIndent = null;
31989         }
31990         this.ui.renderIndent();
31991         if(deep === true && this.childrenRendered){
31992             var cs = this.childNodes;
31993             for(var i = 0, len = cs.length; i < len; i++){
31994                 cs[i].renderIndent(true, refresh);
31995             }
31996         }
31997     }
31998 });/*
31999  * Based on:
32000  * Ext JS Library 1.1.1
32001  * Copyright(c) 2006-2007, Ext JS, LLC.
32002  *
32003  * Originally Released Under LGPL - original licence link has changed is not relivant.
32004  *
32005  * Fork - LGPL
32006  * <script type="text/javascript">
32007  */
32008  
32009 /**
32010  * @class Roo.tree.AsyncTreeNode
32011  * @extends Roo.tree.TreeNode
32012  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32013  * @constructor
32014  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32015  */
32016  Roo.tree.AsyncTreeNode = function(config){
32017     this.loaded = false;
32018     this.loading = false;
32019     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32020     /**
32021     * @event beforeload
32022     * Fires before this node is loaded, return false to cancel
32023     * @param {Node} this This node
32024     */
32025     this.addEvents({'beforeload':true, 'load': true});
32026     /**
32027     * @event load
32028     * Fires when this node is loaded
32029     * @param {Node} this This node
32030     */
32031     /**
32032      * The loader used by this node (defaults to using the tree's defined loader)
32033      * @type TreeLoader
32034      * @property loader
32035      */
32036 };
32037 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32038     expand : function(deep, anim, callback){
32039         if(this.loading){ // if an async load is already running, waiting til it's done
32040             var timer;
32041             var f = function(){
32042                 if(!this.loading){ // done loading
32043                     clearInterval(timer);
32044                     this.expand(deep, anim, callback);
32045                 }
32046             }.createDelegate(this);
32047             timer = setInterval(f, 200);
32048             return;
32049         }
32050         if(!this.loaded){
32051             if(this.fireEvent("beforeload", this) === false){
32052                 return;
32053             }
32054             this.loading = true;
32055             this.ui.beforeLoad(this);
32056             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32057             if(loader){
32058                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32059                 return;
32060             }
32061         }
32062         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32063     },
32064     
32065     /**
32066      * Returns true if this node is currently loading
32067      * @return {Boolean}
32068      */
32069     isLoading : function(){
32070         return this.loading;  
32071     },
32072     
32073     loadComplete : function(deep, anim, callback){
32074         this.loading = false;
32075         this.loaded = true;
32076         this.ui.afterLoad(this);
32077         this.fireEvent("load", this);
32078         this.expand(deep, anim, callback);
32079     },
32080     
32081     /**
32082      * Returns true if this node has been loaded
32083      * @return {Boolean}
32084      */
32085     isLoaded : function(){
32086         return this.loaded;
32087     },
32088     
32089     hasChildNodes : function(){
32090         if(!this.isLeaf() && !this.loaded){
32091             return true;
32092         }else{
32093             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32094         }
32095     },
32096
32097     /**
32098      * Trigger a reload for this node
32099      * @param {Function} callback
32100      */
32101     reload : function(callback){
32102         this.collapse(false, false);
32103         while(this.firstChild){
32104             this.removeChild(this.firstChild);
32105         }
32106         this.childrenRendered = false;
32107         this.loaded = false;
32108         if(this.isHiddenRoot()){
32109             this.expanded = false;
32110         }
32111         this.expand(false, false, callback);
32112     }
32113 });/*
32114  * Based on:
32115  * Ext JS Library 1.1.1
32116  * Copyright(c) 2006-2007, Ext JS, LLC.
32117  *
32118  * Originally Released Under LGPL - original licence link has changed is not relivant.
32119  *
32120  * Fork - LGPL
32121  * <script type="text/javascript">
32122  */
32123  
32124 /**
32125  * @class Roo.tree.TreeNodeUI
32126  * @constructor
32127  * @param {Object} node The node to render
32128  * The TreeNode UI implementation is separate from the
32129  * tree implementation. Unless you are customizing the tree UI,
32130  * you should never have to use this directly.
32131  */
32132 Roo.tree.TreeNodeUI = function(node){
32133     this.node = node;
32134     this.rendered = false;
32135     this.animating = false;
32136     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32137 };
32138
32139 Roo.tree.TreeNodeUI.prototype = {
32140     removeChild : function(node){
32141         if(this.rendered){
32142             this.ctNode.removeChild(node.ui.getEl());
32143         }
32144     },
32145
32146     beforeLoad : function(){
32147          this.addClass("x-tree-node-loading");
32148     },
32149
32150     afterLoad : function(){
32151          this.removeClass("x-tree-node-loading");
32152     },
32153
32154     onTextChange : function(node, text, oldText){
32155         if(this.rendered){
32156             this.textNode.innerHTML = text;
32157         }
32158     },
32159
32160     onDisableChange : function(node, state){
32161         this.disabled = state;
32162         if(state){
32163             this.addClass("x-tree-node-disabled");
32164         }else{
32165             this.removeClass("x-tree-node-disabled");
32166         }
32167     },
32168
32169     onSelectedChange : function(state){
32170         if(state){
32171             this.focus();
32172             this.addClass("x-tree-selected");
32173         }else{
32174             //this.blur();
32175             this.removeClass("x-tree-selected");
32176         }
32177     },
32178
32179     onMove : function(tree, node, oldParent, newParent, index, refNode){
32180         this.childIndent = null;
32181         if(this.rendered){
32182             var targetNode = newParent.ui.getContainer();
32183             if(!targetNode){//target not rendered
32184                 this.holder = document.createElement("div");
32185                 this.holder.appendChild(this.wrap);
32186                 return;
32187             }
32188             var insertBefore = refNode ? refNode.ui.getEl() : null;
32189             if(insertBefore){
32190                 targetNode.insertBefore(this.wrap, insertBefore);
32191             }else{
32192                 targetNode.appendChild(this.wrap);
32193             }
32194             this.node.renderIndent(true);
32195         }
32196     },
32197
32198     addClass : function(cls){
32199         if(this.elNode){
32200             Roo.fly(this.elNode).addClass(cls);
32201         }
32202     },
32203
32204     removeClass : function(cls){
32205         if(this.elNode){
32206             Roo.fly(this.elNode).removeClass(cls);
32207         }
32208     },
32209
32210     remove : function(){
32211         if(this.rendered){
32212             this.holder = document.createElement("div");
32213             this.holder.appendChild(this.wrap);
32214         }
32215     },
32216
32217     fireEvent : function(){
32218         return this.node.fireEvent.apply(this.node, arguments);
32219     },
32220
32221     initEvents : function(){
32222         this.node.on("move", this.onMove, this);
32223         var E = Roo.EventManager;
32224         var a = this.anchor;
32225
32226         var el = Roo.fly(a, '_treeui');
32227
32228         if(Roo.isOpera){ // opera render bug ignores the CSS
32229             el.setStyle("text-decoration", "none");
32230         }
32231
32232         el.on("click", this.onClick, this);
32233         el.on("dblclick", this.onDblClick, this);
32234
32235         if(this.checkbox){
32236             Roo.EventManager.on(this.checkbox,
32237                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32238         }
32239
32240         el.on("contextmenu", this.onContextMenu, this);
32241
32242         var icon = Roo.fly(this.iconNode);
32243         icon.on("click", this.onClick, this);
32244         icon.on("dblclick", this.onDblClick, this);
32245         icon.on("contextmenu", this.onContextMenu, this);
32246         E.on(this.ecNode, "click", this.ecClick, this, true);
32247
32248         if(this.node.disabled){
32249             this.addClass("x-tree-node-disabled");
32250         }
32251         if(this.node.hidden){
32252             this.addClass("x-tree-node-disabled");
32253         }
32254         var ot = this.node.getOwnerTree();
32255         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32256         if(dd && (!this.node.isRoot || ot.rootVisible)){
32257             Roo.dd.Registry.register(this.elNode, {
32258                 node: this.node,
32259                 handles: this.getDDHandles(),
32260                 isHandle: false
32261             });
32262         }
32263     },
32264
32265     getDDHandles : function(){
32266         return [this.iconNode, this.textNode];
32267     },
32268
32269     hide : function(){
32270         if(this.rendered){
32271             this.wrap.style.display = "none";
32272         }
32273     },
32274
32275     show : function(){
32276         if(this.rendered){
32277             this.wrap.style.display = "";
32278         }
32279     },
32280
32281     onContextMenu : function(e){
32282         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32283             e.preventDefault();
32284             this.focus();
32285             this.fireEvent("contextmenu", this.node, e);
32286         }
32287     },
32288
32289     onClick : function(e){
32290         if(this.dropping){
32291             e.stopEvent();
32292             return;
32293         }
32294         if(this.fireEvent("beforeclick", this.node, e) !== false){
32295             if(!this.disabled && this.node.attributes.href){
32296                 this.fireEvent("click", this.node, e);
32297                 return;
32298             }
32299             e.preventDefault();
32300             if(this.disabled){
32301                 return;
32302             }
32303
32304             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32305                 this.node.toggle();
32306             }
32307
32308             this.fireEvent("click", this.node, e);
32309         }else{
32310             e.stopEvent();
32311         }
32312     },
32313
32314     onDblClick : function(e){
32315         e.preventDefault();
32316         if(this.disabled){
32317             return;
32318         }
32319         if(this.checkbox){
32320             this.toggleCheck();
32321         }
32322         if(!this.animating && this.node.hasChildNodes()){
32323             this.node.toggle();
32324         }
32325         this.fireEvent("dblclick", this.node, e);
32326     },
32327
32328     onCheckChange : function(){
32329         var checked = this.checkbox.checked;
32330         this.node.attributes.checked = checked;
32331         this.fireEvent('checkchange', this.node, checked);
32332     },
32333
32334     ecClick : function(e){
32335         if(!this.animating && this.node.hasChildNodes()){
32336             this.node.toggle();
32337         }
32338     },
32339
32340     startDrop : function(){
32341         this.dropping = true;
32342     },
32343
32344     // delayed drop so the click event doesn't get fired on a drop
32345     endDrop : function(){
32346        setTimeout(function(){
32347            this.dropping = false;
32348        }.createDelegate(this), 50);
32349     },
32350
32351     expand : function(){
32352         this.updateExpandIcon();
32353         this.ctNode.style.display = "";
32354     },
32355
32356     focus : function(){
32357         if(!this.node.preventHScroll){
32358             try{this.anchor.focus();
32359             }catch(e){}
32360         }else if(!Roo.isIE){
32361             try{
32362                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32363                 var l = noscroll.scrollLeft;
32364                 this.anchor.focus();
32365                 noscroll.scrollLeft = l;
32366             }catch(e){}
32367         }
32368     },
32369
32370     toggleCheck : function(value){
32371         var cb = this.checkbox;
32372         if(cb){
32373             cb.checked = (value === undefined ? !cb.checked : value);
32374         }
32375     },
32376
32377     blur : function(){
32378         try{
32379             this.anchor.blur();
32380         }catch(e){}
32381     },
32382
32383     animExpand : function(callback){
32384         var ct = Roo.get(this.ctNode);
32385         ct.stopFx();
32386         if(!this.node.hasChildNodes()){
32387             this.updateExpandIcon();
32388             this.ctNode.style.display = "";
32389             Roo.callback(callback);
32390             return;
32391         }
32392         this.animating = true;
32393         this.updateExpandIcon();
32394
32395         ct.slideIn('t', {
32396            callback : function(){
32397                this.animating = false;
32398                Roo.callback(callback);
32399             },
32400             scope: this,
32401             duration: this.node.ownerTree.duration || .25
32402         });
32403     },
32404
32405     highlight : function(){
32406         var tree = this.node.getOwnerTree();
32407         Roo.fly(this.wrap).highlight(
32408             tree.hlColor || "C3DAF9",
32409             {endColor: tree.hlBaseColor}
32410         );
32411     },
32412
32413     collapse : function(){
32414         this.updateExpandIcon();
32415         this.ctNode.style.display = "none";
32416     },
32417
32418     animCollapse : function(callback){
32419         var ct = Roo.get(this.ctNode);
32420         ct.enableDisplayMode('block');
32421         ct.stopFx();
32422
32423         this.animating = true;
32424         this.updateExpandIcon();
32425
32426         ct.slideOut('t', {
32427             callback : function(){
32428                this.animating = false;
32429                Roo.callback(callback);
32430             },
32431             scope: this,
32432             duration: this.node.ownerTree.duration || .25
32433         });
32434     },
32435
32436     getContainer : function(){
32437         return this.ctNode;
32438     },
32439
32440     getEl : function(){
32441         return this.wrap;
32442     },
32443
32444     appendDDGhost : function(ghostNode){
32445         ghostNode.appendChild(this.elNode.cloneNode(true));
32446     },
32447
32448     getDDRepairXY : function(){
32449         return Roo.lib.Dom.getXY(this.iconNode);
32450     },
32451
32452     onRender : function(){
32453         this.render();
32454     },
32455
32456     render : function(bulkRender){
32457         var n = this.node, a = n.attributes;
32458         var targetNode = n.parentNode ?
32459               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32460
32461         if(!this.rendered){
32462             this.rendered = true;
32463
32464             this.renderElements(n, a, targetNode, bulkRender);
32465
32466             if(a.qtip){
32467                if(this.textNode.setAttributeNS){
32468                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32469                    if(a.qtipTitle){
32470                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32471                    }
32472                }else{
32473                    this.textNode.setAttribute("ext:qtip", a.qtip);
32474                    if(a.qtipTitle){
32475                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32476                    }
32477                }
32478             }else if(a.qtipCfg){
32479                 a.qtipCfg.target = Roo.id(this.textNode);
32480                 Roo.QuickTips.register(a.qtipCfg);
32481             }
32482             this.initEvents();
32483             if(!this.node.expanded){
32484                 this.updateExpandIcon();
32485             }
32486         }else{
32487             if(bulkRender === true) {
32488                 targetNode.appendChild(this.wrap);
32489             }
32490         }
32491     },
32492
32493     renderElements : function(n, a, targetNode, bulkRender){
32494         // add some indent caching, this helps performance when rendering a large tree
32495         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32496         var t = n.getOwnerTree();
32497         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32498         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32499         var cb = typeof a.checked == 'boolean';
32500         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32501         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32502             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32503             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32504             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32505             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32506             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32507              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32508                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32509             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32510             "</li>"];
32511
32512         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32513             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32514                                 n.nextSibling.ui.getEl(), buf.join(""));
32515         }else{
32516             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32517         }
32518
32519         this.elNode = this.wrap.childNodes[0];
32520         this.ctNode = this.wrap.childNodes[1];
32521         var cs = this.elNode.childNodes;
32522         this.indentNode = cs[0];
32523         this.ecNode = cs[1];
32524         this.iconNode = cs[2];
32525         var index = 3;
32526         if(cb){
32527             this.checkbox = cs[3];
32528             index++;
32529         }
32530         this.anchor = cs[index];
32531         this.textNode = cs[index].firstChild;
32532     },
32533
32534     getAnchor : function(){
32535         return this.anchor;
32536     },
32537
32538     getTextEl : function(){
32539         return this.textNode;
32540     },
32541
32542     getIconEl : function(){
32543         return this.iconNode;
32544     },
32545
32546     isChecked : function(){
32547         return this.checkbox ? this.checkbox.checked : false;
32548     },
32549
32550     updateExpandIcon : function(){
32551         if(this.rendered){
32552             var n = this.node, c1, c2;
32553             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32554             var hasChild = n.hasChildNodes();
32555             if(hasChild){
32556                 if(n.expanded){
32557                     cls += "-minus";
32558                     c1 = "x-tree-node-collapsed";
32559                     c2 = "x-tree-node-expanded";
32560                 }else{
32561                     cls += "-plus";
32562                     c1 = "x-tree-node-expanded";
32563                     c2 = "x-tree-node-collapsed";
32564                 }
32565                 if(this.wasLeaf){
32566                     this.removeClass("x-tree-node-leaf");
32567                     this.wasLeaf = false;
32568                 }
32569                 if(this.c1 != c1 || this.c2 != c2){
32570                     Roo.fly(this.elNode).replaceClass(c1, c2);
32571                     this.c1 = c1; this.c2 = c2;
32572                 }
32573             }else{
32574                 if(!this.wasLeaf){
32575                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32576                     delete this.c1;
32577                     delete this.c2;
32578                     this.wasLeaf = true;
32579                 }
32580             }
32581             var ecc = "x-tree-ec-icon "+cls;
32582             if(this.ecc != ecc){
32583                 this.ecNode.className = ecc;
32584                 this.ecc = ecc;
32585             }
32586         }
32587     },
32588
32589     getChildIndent : function(){
32590         if(!this.childIndent){
32591             var buf = [];
32592             var p = this.node;
32593             while(p){
32594                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32595                     if(!p.isLast()) {
32596                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32597                     } else {
32598                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32599                     }
32600                 }
32601                 p = p.parentNode;
32602             }
32603             this.childIndent = buf.join("");
32604         }
32605         return this.childIndent;
32606     },
32607
32608     renderIndent : function(){
32609         if(this.rendered){
32610             var indent = "";
32611             var p = this.node.parentNode;
32612             if(p){
32613                 indent = p.ui.getChildIndent();
32614             }
32615             if(this.indentMarkup != indent){ // don't rerender if not required
32616                 this.indentNode.innerHTML = indent;
32617                 this.indentMarkup = indent;
32618             }
32619             this.updateExpandIcon();
32620         }
32621     }
32622 };
32623
32624 Roo.tree.RootTreeNodeUI = function(){
32625     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32626 };
32627 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32628     render : function(){
32629         if(!this.rendered){
32630             var targetNode = this.node.ownerTree.innerCt.dom;
32631             this.node.expanded = true;
32632             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32633             this.wrap = this.ctNode = targetNode.firstChild;
32634         }
32635     },
32636     collapse : function(){
32637     },
32638     expand : function(){
32639     }
32640 });/*
32641  * Based on:
32642  * Ext JS Library 1.1.1
32643  * Copyright(c) 2006-2007, Ext JS, LLC.
32644  *
32645  * Originally Released Under LGPL - original licence link has changed is not relivant.
32646  *
32647  * Fork - LGPL
32648  * <script type="text/javascript">
32649  */
32650 /**
32651  * @class Roo.tree.TreeLoader
32652  * @extends Roo.util.Observable
32653  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32654  * nodes from a specified URL. The response must be a javascript Array definition
32655  * who's elements are node definition objects. eg:
32656  * <pre><code>
32657    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32658     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32659 </code></pre>
32660  * <br><br>
32661  * A server request is sent, and child nodes are loaded only when a node is expanded.
32662  * The loading node's id is passed to the server under the parameter name "node" to
32663  * enable the server to produce the correct child nodes.
32664  * <br><br>
32665  * To pass extra parameters, an event handler may be attached to the "beforeload"
32666  * event, and the parameters specified in the TreeLoader's baseParams property:
32667  * <pre><code>
32668     myTreeLoader.on("beforeload", function(treeLoader, node) {
32669         this.baseParams.category = node.attributes.category;
32670     }, this);
32671 </code></pre><
32672  * This would pass an HTTP parameter called "category" to the server containing
32673  * the value of the Node's "category" attribute.
32674  * @constructor
32675  * Creates a new Treeloader.
32676  * @param {Object} config A config object containing config properties.
32677  */
32678 Roo.tree.TreeLoader = function(config){
32679     this.baseParams = {};
32680     this.requestMethod = "POST";
32681     Roo.apply(this, config);
32682
32683     this.addEvents({
32684     
32685         /**
32686          * @event beforeload
32687          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32688          * @param {Object} This TreeLoader object.
32689          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32690          * @param {Object} callback The callback function specified in the {@link #load} call.
32691          */
32692         beforeload : true,
32693         /**
32694          * @event load
32695          * Fires when the node has been successfuly loaded.
32696          * @param {Object} This TreeLoader object.
32697          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32698          * @param {Object} response The response object containing the data from the server.
32699          */
32700         load : true,
32701         /**
32702          * @event loadexception
32703          * Fires if the network request failed.
32704          * @param {Object} This TreeLoader object.
32705          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32706          * @param {Object} response The response object containing the data from the server.
32707          */
32708         loadexception : true,
32709         /**
32710          * @event create
32711          * Fires before a node is created, enabling you to return custom Node types 
32712          * @param {Object} This TreeLoader object.
32713          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32714          */
32715         create : true
32716     });
32717
32718     Roo.tree.TreeLoader.superclass.constructor.call(this);
32719 };
32720
32721 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32722     /**
32723     * @cfg {String} dataUrl The URL from which to request a Json string which
32724     * specifies an array of node definition object representing the child nodes
32725     * to be loaded.
32726     */
32727     /**
32728     * @cfg {Object} baseParams (optional) An object containing properties which
32729     * specify HTTP parameters to be passed to each request for child nodes.
32730     */
32731     /**
32732     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32733     * created by this loader. If the attributes sent by the server have an attribute in this object,
32734     * they take priority.
32735     */
32736     /**
32737     * @cfg {Object} uiProviders (optional) An object containing properties which
32738     * 
32739     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32740     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32741     * <i>uiProvider</i> attribute of a returned child node is a string rather
32742     * than a reference to a TreeNodeUI implementation, this that string value
32743     * is used as a property name in the uiProviders object. You can define the provider named
32744     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32745     */
32746     uiProviders : {},
32747
32748     /**
32749     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32750     * child nodes before loading.
32751     */
32752     clearOnLoad : true,
32753
32754     /**
32755     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32756     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32757     * Grid query { data : [ .....] }
32758     */
32759     
32760     root : false,
32761      /**
32762     * @cfg {String} queryParam (optional) 
32763     * Name of the query as it will be passed on the querystring (defaults to 'node')
32764     * eg. the request will be ?node=[id]
32765     */
32766     
32767     
32768     queryParam: false,
32769     
32770     /**
32771      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32772      * This is called automatically when a node is expanded, but may be used to reload
32773      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32774      * @param {Roo.tree.TreeNode} node
32775      * @param {Function} callback
32776      */
32777     load : function(node, callback){
32778         if(this.clearOnLoad){
32779             while(node.firstChild){
32780                 node.removeChild(node.firstChild);
32781             }
32782         }
32783         if(node.attributes.children){ // preloaded json children
32784             var cs = node.attributes.children;
32785             for(var i = 0, len = cs.length; i < len; i++){
32786                 node.appendChild(this.createNode(cs[i]));
32787             }
32788             if(typeof callback == "function"){
32789                 callback();
32790             }
32791         }else if(this.dataUrl){
32792             this.requestData(node, callback);
32793         }
32794     },
32795
32796     getParams: function(node){
32797         var buf = [], bp = this.baseParams;
32798         for(var key in bp){
32799             if(typeof bp[key] != "function"){
32800                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32801             }
32802         }
32803         var n = this.queryParam === false ? 'node' : this.queryParam;
32804         buf.push(n + "=", encodeURIComponent(node.id));
32805         return buf.join("");
32806     },
32807
32808     requestData : function(node, callback){
32809         if(this.fireEvent("beforeload", this, node, callback) !== false){
32810             this.transId = Roo.Ajax.request({
32811                 method:this.requestMethod,
32812                 url: this.dataUrl||this.url,
32813                 success: this.handleResponse,
32814                 failure: this.handleFailure,
32815                 scope: this,
32816                 argument: {callback: callback, node: node},
32817                 params: this.getParams(node)
32818             });
32819         }else{
32820             // if the load is cancelled, make sure we notify
32821             // the node that we are done
32822             if(typeof callback == "function"){
32823                 callback();
32824             }
32825         }
32826     },
32827
32828     isLoading : function(){
32829         return this.transId ? true : false;
32830     },
32831
32832     abort : function(){
32833         if(this.isLoading()){
32834             Roo.Ajax.abort(this.transId);
32835         }
32836     },
32837
32838     // private
32839     createNode : function(attr){
32840         // apply baseAttrs, nice idea Corey!
32841         if(this.baseAttrs){
32842             Roo.applyIf(attr, this.baseAttrs);
32843         }
32844         if(this.applyLoader !== false){
32845             attr.loader = this;
32846         }
32847         // uiProvider = depreciated..
32848         
32849         if(typeof(attr.uiProvider) == 'string'){
32850            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32851                 /**  eval:var:attr */ eval(attr.uiProvider);
32852         }
32853         if(typeof(this.uiProviders['default']) != 'undefined') {
32854             attr.uiProvider = this.uiProviders['default'];
32855         }
32856         
32857         this.fireEvent('create', this, attr);
32858         
32859         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32860         return(attr.leaf ?
32861                         new Roo.tree.TreeNode(attr) :
32862                         new Roo.tree.AsyncTreeNode(attr));
32863     },
32864
32865     processResponse : function(response, node, callback){
32866         var json = response.responseText;
32867         try {
32868             
32869             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32870             if (this.root !== false) {
32871                 o = o[this.root];
32872             }
32873             
32874             for(var i = 0, len = o.length; i < len; i++){
32875                 var n = this.createNode(o[i]);
32876                 if(n){
32877                     node.appendChild(n);
32878                 }
32879             }
32880             if(typeof callback == "function"){
32881                 callback(this, node);
32882             }
32883         }catch(e){
32884             this.handleFailure(response);
32885         }
32886     },
32887
32888     handleResponse : function(response){
32889         this.transId = false;
32890         var a = response.argument;
32891         this.processResponse(response, a.node, a.callback);
32892         this.fireEvent("load", this, a.node, response);
32893     },
32894
32895     handleFailure : function(response){
32896         this.transId = false;
32897         var a = response.argument;
32898         this.fireEvent("loadexception", this, a.node, response);
32899         if(typeof a.callback == "function"){
32900             a.callback(this, a.node);
32901         }
32902     }
32903 });/*
32904  * Based on:
32905  * Ext JS Library 1.1.1
32906  * Copyright(c) 2006-2007, Ext JS, LLC.
32907  *
32908  * Originally Released Under LGPL - original licence link has changed is not relivant.
32909  *
32910  * Fork - LGPL
32911  * <script type="text/javascript">
32912  */
32913
32914 /**
32915 * @class Roo.tree.TreeFilter
32916 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32917 * @param {TreePanel} tree
32918 * @param {Object} config (optional)
32919  */
32920 Roo.tree.TreeFilter = function(tree, config){
32921     this.tree = tree;
32922     this.filtered = {};
32923     Roo.apply(this, config);
32924 };
32925
32926 Roo.tree.TreeFilter.prototype = {
32927     clearBlank:false,
32928     reverse:false,
32929     autoClear:false,
32930     remove:false,
32931
32932      /**
32933      * Filter the data by a specific attribute.
32934      * @param {String/RegExp} value Either string that the attribute value
32935      * should start with or a RegExp to test against the attribute
32936      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32937      * @param {TreeNode} startNode (optional) The node to start the filter at.
32938      */
32939     filter : function(value, attr, startNode){
32940         attr = attr || "text";
32941         var f;
32942         if(typeof value == "string"){
32943             var vlen = value.length;
32944             // auto clear empty filter
32945             if(vlen == 0 && this.clearBlank){
32946                 this.clear();
32947                 return;
32948             }
32949             value = value.toLowerCase();
32950             f = function(n){
32951                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32952             };
32953         }else if(value.exec){ // regex?
32954             f = function(n){
32955                 return value.test(n.attributes[attr]);
32956             };
32957         }else{
32958             throw 'Illegal filter type, must be string or regex';
32959         }
32960         this.filterBy(f, null, startNode);
32961         },
32962
32963     /**
32964      * Filter by a function. The passed function will be called with each
32965      * node in the tree (or from the startNode). If the function returns true, the node is kept
32966      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32967      * @param {Function} fn The filter function
32968      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32969      */
32970     filterBy : function(fn, scope, startNode){
32971         startNode = startNode || this.tree.root;
32972         if(this.autoClear){
32973             this.clear();
32974         }
32975         var af = this.filtered, rv = this.reverse;
32976         var f = function(n){
32977             if(n == startNode){
32978                 return true;
32979             }
32980             if(af[n.id]){
32981                 return false;
32982             }
32983             var m = fn.call(scope || n, n);
32984             if(!m || rv){
32985                 af[n.id] = n;
32986                 n.ui.hide();
32987                 return false;
32988             }
32989             return true;
32990         };
32991         startNode.cascade(f);
32992         if(this.remove){
32993            for(var id in af){
32994                if(typeof id != "function"){
32995                    var n = af[id];
32996                    if(n && n.parentNode){
32997                        n.parentNode.removeChild(n);
32998                    }
32999                }
33000            }
33001         }
33002     },
33003
33004     /**
33005      * Clears the current filter. Note: with the "remove" option
33006      * set a filter cannot be cleared.
33007      */
33008     clear : function(){
33009         var t = this.tree;
33010         var af = this.filtered;
33011         for(var id in af){
33012             if(typeof id != "function"){
33013                 var n = af[id];
33014                 if(n){
33015                     n.ui.show();
33016                 }
33017             }
33018         }
33019         this.filtered = {};
33020     }
33021 };
33022 /*
33023  * Based on:
33024  * Ext JS Library 1.1.1
33025  * Copyright(c) 2006-2007, Ext JS, LLC.
33026  *
33027  * Originally Released Under LGPL - original licence link has changed is not relivant.
33028  *
33029  * Fork - LGPL
33030  * <script type="text/javascript">
33031  */
33032  
33033
33034 /**
33035  * @class Roo.tree.TreeSorter
33036  * Provides sorting of nodes in a TreePanel
33037  * 
33038  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33039  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33040  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33041  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33042  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33043  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33044  * @constructor
33045  * @param {TreePanel} tree
33046  * @param {Object} config
33047  */
33048 Roo.tree.TreeSorter = function(tree, config){
33049     Roo.apply(this, config);
33050     tree.on("beforechildrenrendered", this.doSort, this);
33051     tree.on("append", this.updateSort, this);
33052     tree.on("insert", this.updateSort, this);
33053     
33054     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33055     var p = this.property || "text";
33056     var sortType = this.sortType;
33057     var fs = this.folderSort;
33058     var cs = this.caseSensitive === true;
33059     var leafAttr = this.leafAttr || 'leaf';
33060
33061     this.sortFn = function(n1, n2){
33062         if(fs){
33063             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33064                 return 1;
33065             }
33066             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33067                 return -1;
33068             }
33069         }
33070         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33071         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33072         if(v1 < v2){
33073                         return dsc ? +1 : -1;
33074                 }else if(v1 > v2){
33075                         return dsc ? -1 : +1;
33076         }else{
33077                 return 0;
33078         }
33079     };
33080 };
33081
33082 Roo.tree.TreeSorter.prototype = {
33083     doSort : function(node){
33084         node.sort(this.sortFn);
33085     },
33086     
33087     compareNodes : function(n1, n2){
33088         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33089     },
33090     
33091     updateSort : function(tree, node){
33092         if(node.childrenRendered){
33093             this.doSort.defer(1, this, [node]);
33094         }
33095     }
33096 };/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106
33107 if(Roo.dd.DropZone){
33108     
33109 Roo.tree.TreeDropZone = function(tree, config){
33110     this.allowParentInsert = false;
33111     this.allowContainerDrop = false;
33112     this.appendOnly = false;
33113     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33114     this.tree = tree;
33115     this.lastInsertClass = "x-tree-no-status";
33116     this.dragOverData = {};
33117 };
33118
33119 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33120     ddGroup : "TreeDD",
33121     
33122     expandDelay : 1000,
33123     
33124     expandNode : function(node){
33125         if(node.hasChildNodes() && !node.isExpanded()){
33126             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33127         }
33128     },
33129     
33130     queueExpand : function(node){
33131         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33132     },
33133     
33134     cancelExpand : function(){
33135         if(this.expandProcId){
33136             clearTimeout(this.expandProcId);
33137             this.expandProcId = false;
33138         }
33139     },
33140     
33141     isValidDropPoint : function(n, pt, dd, e, data){
33142         if(!n || !data){ return false; }
33143         var targetNode = n.node;
33144         var dropNode = data.node;
33145         // default drop rules
33146         if(!(targetNode && targetNode.isTarget && pt)){
33147             return false;
33148         }
33149         if(pt == "append" && targetNode.allowChildren === false){
33150             return false;
33151         }
33152         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33153             return false;
33154         }
33155         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33156             return false;
33157         }
33158         // reuse the object
33159         var overEvent = this.dragOverData;
33160         overEvent.tree = this.tree;
33161         overEvent.target = targetNode;
33162         overEvent.data = data;
33163         overEvent.point = pt;
33164         overEvent.source = dd;
33165         overEvent.rawEvent = e;
33166         overEvent.dropNode = dropNode;
33167         overEvent.cancel = false;  
33168         var result = this.tree.fireEvent("nodedragover", overEvent);
33169         return overEvent.cancel === false && result !== false;
33170     },
33171     
33172     getDropPoint : function(e, n, dd){
33173         var tn = n.node;
33174         if(tn.isRoot){
33175             return tn.allowChildren !== false ? "append" : false; // always append for root
33176         }
33177         var dragEl = n.ddel;
33178         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33179         var y = Roo.lib.Event.getPageY(e);
33180         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33181         
33182         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33183         var noAppend = tn.allowChildren === false;
33184         if(this.appendOnly || tn.parentNode.allowChildren === false){
33185             return noAppend ? false : "append";
33186         }
33187         var noBelow = false;
33188         if(!this.allowParentInsert){
33189             noBelow = tn.hasChildNodes() && tn.isExpanded();
33190         }
33191         var q = (b - t) / (noAppend ? 2 : 3);
33192         if(y >= t && y < (t + q)){
33193             return "above";
33194         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33195             return "below";
33196         }else{
33197             return "append";
33198         }
33199     },
33200     
33201     onNodeEnter : function(n, dd, e, data){
33202         this.cancelExpand();
33203     },
33204     
33205     onNodeOver : function(n, dd, e, data){
33206         var pt = this.getDropPoint(e, n, dd);
33207         var node = n.node;
33208         
33209         // auto node expand check
33210         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33211             this.queueExpand(node);
33212         }else if(pt != "append"){
33213             this.cancelExpand();
33214         }
33215         
33216         // set the insert point style on the target node
33217         var returnCls = this.dropNotAllowed;
33218         if(this.isValidDropPoint(n, pt, dd, e, data)){
33219            if(pt){
33220                var el = n.ddel;
33221                var cls;
33222                if(pt == "above"){
33223                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33224                    cls = "x-tree-drag-insert-above";
33225                }else if(pt == "below"){
33226                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33227                    cls = "x-tree-drag-insert-below";
33228                }else{
33229                    returnCls = "x-tree-drop-ok-append";
33230                    cls = "x-tree-drag-append";
33231                }
33232                if(this.lastInsertClass != cls){
33233                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33234                    this.lastInsertClass = cls;
33235                }
33236            }
33237        }
33238        return returnCls;
33239     },
33240     
33241     onNodeOut : function(n, dd, e, data){
33242         this.cancelExpand();
33243         this.removeDropIndicators(n);
33244     },
33245     
33246     onNodeDrop : function(n, dd, e, data){
33247         var point = this.getDropPoint(e, n, dd);
33248         var targetNode = n.node;
33249         targetNode.ui.startDrop();
33250         if(!this.isValidDropPoint(n, point, dd, e, data)){
33251             targetNode.ui.endDrop();
33252             return false;
33253         }
33254         // first try to find the drop node
33255         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33256         var dropEvent = {
33257             tree : this.tree,
33258             target: targetNode,
33259             data: data,
33260             point: point,
33261             source: dd,
33262             rawEvent: e,
33263             dropNode: dropNode,
33264             cancel: !dropNode   
33265         };
33266         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33267         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33268             targetNode.ui.endDrop();
33269             return false;
33270         }
33271         // allow target changing
33272         targetNode = dropEvent.target;
33273         if(point == "append" && !targetNode.isExpanded()){
33274             targetNode.expand(false, null, function(){
33275                 this.completeDrop(dropEvent);
33276             }.createDelegate(this));
33277         }else{
33278             this.completeDrop(dropEvent);
33279         }
33280         return true;
33281     },
33282     
33283     completeDrop : function(de){
33284         var ns = de.dropNode, p = de.point, t = de.target;
33285         if(!(ns instanceof Array)){
33286             ns = [ns];
33287         }
33288         var n;
33289         for(var i = 0, len = ns.length; i < len; i++){
33290             n = ns[i];
33291             if(p == "above"){
33292                 t.parentNode.insertBefore(n, t);
33293             }else if(p == "below"){
33294                 t.parentNode.insertBefore(n, t.nextSibling);
33295             }else{
33296                 t.appendChild(n);
33297             }
33298         }
33299         n.ui.focus();
33300         if(this.tree.hlDrop){
33301             n.ui.highlight();
33302         }
33303         t.ui.endDrop();
33304         this.tree.fireEvent("nodedrop", de);
33305     },
33306     
33307     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33308         if(this.tree.hlDrop){
33309             dropNode.ui.focus();
33310             dropNode.ui.highlight();
33311         }
33312         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33313     },
33314     
33315     getTree : function(){
33316         return this.tree;
33317     },
33318     
33319     removeDropIndicators : function(n){
33320         if(n && n.ddel){
33321             var el = n.ddel;
33322             Roo.fly(el).removeClass([
33323                     "x-tree-drag-insert-above",
33324                     "x-tree-drag-insert-below",
33325                     "x-tree-drag-append"]);
33326             this.lastInsertClass = "_noclass";
33327         }
33328     },
33329     
33330     beforeDragDrop : function(target, e, id){
33331         this.cancelExpand();
33332         return true;
33333     },
33334     
33335     afterRepair : function(data){
33336         if(data && Roo.enableFx){
33337             data.node.ui.highlight();
33338         }
33339         this.hideProxy();
33340     }    
33341 });
33342
33343 }
33344 /*
33345  * Based on:
33346  * Ext JS Library 1.1.1
33347  * Copyright(c) 2006-2007, Ext JS, LLC.
33348  *
33349  * Originally Released Under LGPL - original licence link has changed is not relivant.
33350  *
33351  * Fork - LGPL
33352  * <script type="text/javascript">
33353  */
33354  
33355
33356 if(Roo.dd.DragZone){
33357 Roo.tree.TreeDragZone = function(tree, config){
33358     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33359     this.tree = tree;
33360 };
33361
33362 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33363     ddGroup : "TreeDD",
33364     
33365     onBeforeDrag : function(data, e){
33366         var n = data.node;
33367         return n && n.draggable && !n.disabled;
33368     },
33369     
33370     onInitDrag : function(e){
33371         var data = this.dragData;
33372         this.tree.getSelectionModel().select(data.node);
33373         this.proxy.update("");
33374         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33375         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33376     },
33377     
33378     getRepairXY : function(e, data){
33379         return data.node.ui.getDDRepairXY();
33380     },
33381     
33382     onEndDrag : function(data, e){
33383         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33384     },
33385     
33386     onValidDrop : function(dd, e, id){
33387         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33388         this.hideProxy();
33389     },
33390     
33391     beforeInvalidDrop : function(e, id){
33392         // this scrolls the original position back into view
33393         var sm = this.tree.getSelectionModel();
33394         sm.clearSelections();
33395         sm.select(this.dragData.node);
33396     }
33397 });
33398 }/*
33399  * Based on:
33400  * Ext JS Library 1.1.1
33401  * Copyright(c) 2006-2007, Ext JS, LLC.
33402  *
33403  * Originally Released Under LGPL - original licence link has changed is not relivant.
33404  *
33405  * Fork - LGPL
33406  * <script type="text/javascript">
33407  */
33408 /**
33409  * @class Roo.tree.TreeEditor
33410  * @extends Roo.Editor
33411  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33412  * as the editor field.
33413  * @constructor
33414  * @param {TreePanel} tree
33415  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33416  */
33417 Roo.tree.TreeEditor = function(tree, config){
33418     config = config || {};
33419     var field = config.events ? config : new Roo.form.TextField(config);
33420     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33421
33422     this.tree = tree;
33423
33424     tree.on('beforeclick', this.beforeNodeClick, this);
33425     tree.getTreeEl().on('mousedown', this.hide, this);
33426     this.on('complete', this.updateNode, this);
33427     this.on('beforestartedit', this.fitToTree, this);
33428     this.on('startedit', this.bindScroll, this, {delay:10});
33429     this.on('specialkey', this.onSpecialKey, this);
33430 };
33431
33432 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33433     /**
33434      * @cfg {String} alignment
33435      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33436      */
33437     alignment: "l-l",
33438     // inherit
33439     autoSize: false,
33440     /**
33441      * @cfg {Boolean} hideEl
33442      * True to hide the bound element while the editor is displayed (defaults to false)
33443      */
33444     hideEl : false,
33445     /**
33446      * @cfg {String} cls
33447      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33448      */
33449     cls: "x-small-editor x-tree-editor",
33450     /**
33451      * @cfg {Boolean} shim
33452      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33453      */
33454     shim:false,
33455     // inherit
33456     shadow:"frame",
33457     /**
33458      * @cfg {Number} maxWidth
33459      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33460      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33461      * scroll and client offsets into account prior to each edit.
33462      */
33463     maxWidth: 250,
33464
33465     editDelay : 350,
33466
33467     // private
33468     fitToTree : function(ed, el){
33469         var td = this.tree.getTreeEl().dom, nd = el.dom;
33470         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33471             td.scrollLeft = nd.offsetLeft;
33472         }
33473         var w = Math.min(
33474                 this.maxWidth,
33475                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33476         this.setSize(w, '');
33477     },
33478
33479     // private
33480     triggerEdit : function(node){
33481         this.completeEdit();
33482         this.editNode = node;
33483         this.startEdit(node.ui.textNode, node.text);
33484     },
33485
33486     // private
33487     bindScroll : function(){
33488         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33489     },
33490
33491     // private
33492     beforeNodeClick : function(node, e){
33493         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33494         this.lastClick = new Date();
33495         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33496             e.stopEvent();
33497             this.triggerEdit(node);
33498             return false;
33499         }
33500     },
33501
33502     // private
33503     updateNode : function(ed, value){
33504         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33505         this.editNode.setText(value);
33506     },
33507
33508     // private
33509     onHide : function(){
33510         Roo.tree.TreeEditor.superclass.onHide.call(this);
33511         if(this.editNode){
33512             this.editNode.ui.focus();
33513         }
33514     },
33515
33516     // private
33517     onSpecialKey : function(field, e){
33518         var k = e.getKey();
33519         if(k == e.ESC){
33520             e.stopEvent();
33521             this.cancelEdit();
33522         }else if(k == e.ENTER && !e.hasModifier()){
33523             e.stopEvent();
33524             this.completeEdit();
33525         }
33526     }
33527 });//<Script type="text/javascript">
33528 /*
33529  * Based on:
33530  * Ext JS Library 1.1.1
33531  * Copyright(c) 2006-2007, Ext JS, LLC.
33532  *
33533  * Originally Released Under LGPL - original licence link has changed is not relivant.
33534  *
33535  * Fork - LGPL
33536  * <script type="text/javascript">
33537  */
33538  
33539 /**
33540  * Not documented??? - probably should be...
33541  */
33542
33543 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33544     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33545     
33546     renderElements : function(n, a, targetNode, bulkRender){
33547         //consel.log("renderElements?");
33548         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33549
33550         var t = n.getOwnerTree();
33551         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33552         
33553         var cols = t.columns;
33554         var bw = t.borderWidth;
33555         var c = cols[0];
33556         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33557          var cb = typeof a.checked == "boolean";
33558         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33559         var colcls = 'x-t-' + tid + '-c0';
33560         var buf = [
33561             '<li class="x-tree-node">',
33562             
33563                 
33564                 '<div class="x-tree-node-el ', a.cls,'">',
33565                     // extran...
33566                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33567                 
33568                 
33569                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33570                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33571                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33572                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33573                            (a.iconCls ? ' '+a.iconCls : ''),
33574                            '" unselectable="on" />',
33575                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33576                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33577                              
33578                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33579                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33580                             '<span unselectable="on" qtip="' + tx + '">',
33581                              tx,
33582                              '</span></a>' ,
33583                     '</div>',
33584                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33585                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33586                  ];
33587         for(var i = 1, len = cols.length; i < len; i++){
33588             c = cols[i];
33589             colcls = 'x-t-' + tid + '-c' +i;
33590             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33591             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33592                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33593                       "</div>");
33594          }
33595          
33596          buf.push(
33597             '</a>',
33598             '<div class="x-clear"></div></div>',
33599             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33600             "</li>");
33601         
33602         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33603             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33604                                 n.nextSibling.ui.getEl(), buf.join(""));
33605         }else{
33606             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33607         }
33608         var el = this.wrap.firstChild;
33609         this.elRow = el;
33610         this.elNode = el.firstChild;
33611         this.ranchor = el.childNodes[1];
33612         this.ctNode = this.wrap.childNodes[1];
33613         var cs = el.firstChild.childNodes;
33614         this.indentNode = cs[0];
33615         this.ecNode = cs[1];
33616         this.iconNode = cs[2];
33617         var index = 3;
33618         if(cb){
33619             this.checkbox = cs[3];
33620             index++;
33621         }
33622         this.anchor = cs[index];
33623         
33624         this.textNode = cs[index].firstChild;
33625         
33626         //el.on("click", this.onClick, this);
33627         //el.on("dblclick", this.onDblClick, this);
33628         
33629         
33630        // console.log(this);
33631     },
33632     initEvents : function(){
33633         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33634         
33635             
33636         var a = this.ranchor;
33637
33638         var el = Roo.get(a);
33639
33640         if(Roo.isOpera){ // opera render bug ignores the CSS
33641             el.setStyle("text-decoration", "none");
33642         }
33643
33644         el.on("click", this.onClick, this);
33645         el.on("dblclick", this.onDblClick, this);
33646         el.on("contextmenu", this.onContextMenu, this);
33647         
33648     },
33649     
33650     /*onSelectedChange : function(state){
33651         if(state){
33652             this.focus();
33653             this.addClass("x-tree-selected");
33654         }else{
33655             //this.blur();
33656             this.removeClass("x-tree-selected");
33657         }
33658     },*/
33659     addClass : function(cls){
33660         if(this.elRow){
33661             Roo.fly(this.elRow).addClass(cls);
33662         }
33663         
33664     },
33665     
33666     
33667     removeClass : function(cls){
33668         if(this.elRow){
33669             Roo.fly(this.elRow).removeClass(cls);
33670         }
33671     }
33672
33673     
33674     
33675 });//<Script type="text/javascript">
33676
33677 /*
33678  * Based on:
33679  * Ext JS Library 1.1.1
33680  * Copyright(c) 2006-2007, Ext JS, LLC.
33681  *
33682  * Originally Released Under LGPL - original licence link has changed is not relivant.
33683  *
33684  * Fork - LGPL
33685  * <script type="text/javascript">
33686  */
33687  
33688
33689 /**
33690  * @class Roo.tree.ColumnTree
33691  * @extends Roo.data.TreePanel
33692  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33693  * @cfg {int} borderWidth  compined right/left border allowance
33694  * @constructor
33695  * @param {String/HTMLElement/Element} el The container element
33696  * @param {Object} config
33697  */
33698 Roo.tree.ColumnTree =  function(el, config)
33699 {
33700    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33701    this.addEvents({
33702         /**
33703         * @event resize
33704         * Fire this event on a container when it resizes
33705         * @param {int} w Width
33706         * @param {int} h Height
33707         */
33708        "resize" : true
33709     });
33710     this.on('resize', this.onResize, this);
33711 };
33712
33713 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33714     //lines:false,
33715     
33716     
33717     borderWidth: Roo.isBorderBox ? 0 : 2, 
33718     headEls : false,
33719     
33720     render : function(){
33721         // add the header.....
33722        
33723         Roo.tree.ColumnTree.superclass.render.apply(this);
33724         
33725         this.el.addClass('x-column-tree');
33726         
33727         this.headers = this.el.createChild(
33728             {cls:'x-tree-headers'},this.innerCt.dom);
33729    
33730         var cols = this.columns, c;
33731         var totalWidth = 0;
33732         this.headEls = [];
33733         var  len = cols.length;
33734         for(var i = 0; i < len; i++){
33735              c = cols[i];
33736              totalWidth += c.width;
33737             this.headEls.push(this.headers.createChild({
33738                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33739                  cn: {
33740                      cls:'x-tree-hd-text',
33741                      html: c.header
33742                  },
33743                  style:'width:'+(c.width-this.borderWidth)+'px;'
33744              }));
33745         }
33746         this.headers.createChild({cls:'x-clear'});
33747         // prevent floats from wrapping when clipped
33748         this.headers.setWidth(totalWidth);
33749         //this.innerCt.setWidth(totalWidth);
33750         this.innerCt.setStyle({ overflow: 'auto' });
33751         this.onResize(this.width, this.height);
33752              
33753         
33754     },
33755     onResize : function(w,h)
33756     {
33757         this.height = h;
33758         this.width = w;
33759         // resize cols..
33760         this.innerCt.setWidth(this.width);
33761         this.innerCt.setHeight(this.height-20);
33762         
33763         // headers...
33764         var cols = this.columns, c;
33765         var totalWidth = 0;
33766         var expEl = false;
33767         var len = cols.length;
33768         for(var i = 0; i < len; i++){
33769             c = cols[i];
33770             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33771                 // it's the expander..
33772                 expEl  = this.headEls[i];
33773                 continue;
33774             }
33775             totalWidth += c.width;
33776             
33777         }
33778         if (expEl) {
33779             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33780         }
33781         this.headers.setWidth(w-20);
33782
33783         
33784         
33785         
33786     }
33787 });
33788 /*
33789  * Based on:
33790  * Ext JS Library 1.1.1
33791  * Copyright(c) 2006-2007, Ext JS, LLC.
33792  *
33793  * Originally Released Under LGPL - original licence link has changed is not relivant.
33794  *
33795  * Fork - LGPL
33796  * <script type="text/javascript">
33797  */
33798  
33799 /**
33800  * @class Roo.menu.Menu
33801  * @extends Roo.util.Observable
33802  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33803  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33804  * @constructor
33805  * Creates a new Menu
33806  * @param {Object} config Configuration options
33807  */
33808 Roo.menu.Menu = function(config){
33809     Roo.apply(this, config);
33810     this.id = this.id || Roo.id();
33811     this.addEvents({
33812         /**
33813          * @event beforeshow
33814          * Fires before this menu is displayed
33815          * @param {Roo.menu.Menu} this
33816          */
33817         beforeshow : true,
33818         /**
33819          * @event beforehide
33820          * Fires before this menu is hidden
33821          * @param {Roo.menu.Menu} this
33822          */
33823         beforehide : true,
33824         /**
33825          * @event show
33826          * Fires after this menu is displayed
33827          * @param {Roo.menu.Menu} this
33828          */
33829         show : true,
33830         /**
33831          * @event hide
33832          * Fires after this menu is hidden
33833          * @param {Roo.menu.Menu} this
33834          */
33835         hide : true,
33836         /**
33837          * @event click
33838          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33839          * @param {Roo.menu.Menu} this
33840          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33841          * @param {Roo.EventObject} e
33842          */
33843         click : true,
33844         /**
33845          * @event mouseover
33846          * Fires when the mouse is hovering over this menu
33847          * @param {Roo.menu.Menu} this
33848          * @param {Roo.EventObject} e
33849          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33850          */
33851         mouseover : true,
33852         /**
33853          * @event mouseout
33854          * Fires when the mouse exits this menu
33855          * @param {Roo.menu.Menu} this
33856          * @param {Roo.EventObject} e
33857          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33858          */
33859         mouseout : true,
33860         /**
33861          * @event itemclick
33862          * Fires when a menu item contained in this menu is clicked
33863          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33864          * @param {Roo.EventObject} e
33865          */
33866         itemclick: true
33867     });
33868     if (this.registerMenu) {
33869         Roo.menu.MenuMgr.register(this);
33870     }
33871     
33872     var mis = this.items;
33873     this.items = new Roo.util.MixedCollection();
33874     if(mis){
33875         this.add.apply(this, mis);
33876     }
33877 };
33878
33879 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33880     /**
33881      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33882      */
33883     minWidth : 120,
33884     /**
33885      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33886      * for bottom-right shadow (defaults to "sides")
33887      */
33888     shadow : "sides",
33889     /**
33890      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33891      * this menu (defaults to "tl-tr?")
33892      */
33893     subMenuAlign : "tl-tr?",
33894     /**
33895      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33896      * relative to its element of origin (defaults to "tl-bl?")
33897      */
33898     defaultAlign : "tl-bl?",
33899     /**
33900      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33901      */
33902     allowOtherMenus : false,
33903     /**
33904      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33905      */
33906     registerMenu : true,
33907
33908     hidden:true,
33909
33910     // private
33911     render : function(){
33912         if(this.el){
33913             return;
33914         }
33915         var el = this.el = new Roo.Layer({
33916             cls: "x-menu",
33917             shadow:this.shadow,
33918             constrain: false,
33919             parentEl: this.parentEl || document.body,
33920             zindex:15000
33921         });
33922
33923         this.keyNav = new Roo.menu.MenuNav(this);
33924
33925         if(this.plain){
33926             el.addClass("x-menu-plain");
33927         }
33928         if(this.cls){
33929             el.addClass(this.cls);
33930         }
33931         // generic focus element
33932         this.focusEl = el.createChild({
33933             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33934         });
33935         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33936         ul.on("click", this.onClick, this);
33937         ul.on("mouseover", this.onMouseOver, this);
33938         ul.on("mouseout", this.onMouseOut, this);
33939         this.items.each(function(item){
33940             var li = document.createElement("li");
33941             li.className = "x-menu-list-item";
33942             ul.dom.appendChild(li);
33943             item.render(li, this);
33944         }, this);
33945         this.ul = ul;
33946         this.autoWidth();
33947     },
33948
33949     // private
33950     autoWidth : function(){
33951         var el = this.el, ul = this.ul;
33952         if(!el){
33953             return;
33954         }
33955         var w = this.width;
33956         if(w){
33957             el.setWidth(w);
33958         }else if(Roo.isIE){
33959             el.setWidth(this.minWidth);
33960             var t = el.dom.offsetWidth; // force recalc
33961             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33962         }
33963     },
33964
33965     // private
33966     delayAutoWidth : function(){
33967         if(this.rendered){
33968             if(!this.awTask){
33969                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33970             }
33971             this.awTask.delay(20);
33972         }
33973     },
33974
33975     // private
33976     findTargetItem : function(e){
33977         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33978         if(t && t.menuItemId){
33979             return this.items.get(t.menuItemId);
33980         }
33981     },
33982
33983     // private
33984     onClick : function(e){
33985         var t;
33986         if(t = this.findTargetItem(e)){
33987             t.onClick(e);
33988             this.fireEvent("click", this, t, e);
33989         }
33990     },
33991
33992     // private
33993     setActiveItem : function(item, autoExpand){
33994         if(item != this.activeItem){
33995             if(this.activeItem){
33996                 this.activeItem.deactivate();
33997             }
33998             this.activeItem = item;
33999             item.activate(autoExpand);
34000         }else if(autoExpand){
34001             item.expandMenu();
34002         }
34003     },
34004
34005     // private
34006     tryActivate : function(start, step){
34007         var items = this.items;
34008         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34009             var item = items.get(i);
34010             if(!item.disabled && item.canActivate){
34011                 this.setActiveItem(item, false);
34012                 return item;
34013             }
34014         }
34015         return false;
34016     },
34017
34018     // private
34019     onMouseOver : function(e){
34020         var t;
34021         if(t = this.findTargetItem(e)){
34022             if(t.canActivate && !t.disabled){
34023                 this.setActiveItem(t, true);
34024             }
34025         }
34026         this.fireEvent("mouseover", this, e, t);
34027     },
34028
34029     // private
34030     onMouseOut : function(e){
34031         var t;
34032         if(t = this.findTargetItem(e)){
34033             if(t == this.activeItem && t.shouldDeactivate(e)){
34034                 this.activeItem.deactivate();
34035                 delete this.activeItem;
34036             }
34037         }
34038         this.fireEvent("mouseout", this, e, t);
34039     },
34040
34041     /**
34042      * Read-only.  Returns true if the menu is currently displayed, else false.
34043      * @type Boolean
34044      */
34045     isVisible : function(){
34046         return this.el && !this.hidden;
34047     },
34048
34049     /**
34050      * Displays this menu relative to another element
34051      * @param {String/HTMLElement/Roo.Element} element The element to align to
34052      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34053      * the element (defaults to this.defaultAlign)
34054      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34055      */
34056     show : function(el, pos, parentMenu){
34057         this.parentMenu = parentMenu;
34058         if(!this.el){
34059             this.render();
34060         }
34061         this.fireEvent("beforeshow", this);
34062         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34063     },
34064
34065     /**
34066      * Displays this menu at a specific xy position
34067      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34068      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34069      */
34070     showAt : function(xy, parentMenu, /* private: */_e){
34071         this.parentMenu = parentMenu;
34072         if(!this.el){
34073             this.render();
34074         }
34075         if(_e !== false){
34076             this.fireEvent("beforeshow", this);
34077             xy = this.el.adjustForConstraints(xy);
34078         }
34079         this.el.setXY(xy);
34080         this.el.show();
34081         this.hidden = false;
34082         this.focus();
34083         this.fireEvent("show", this);
34084     },
34085
34086     focus : function(){
34087         if(!this.hidden){
34088             this.doFocus.defer(50, this);
34089         }
34090     },
34091
34092     doFocus : function(){
34093         if(!this.hidden){
34094             this.focusEl.focus();
34095         }
34096     },
34097
34098     /**
34099      * Hides this menu and optionally all parent menus
34100      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34101      */
34102     hide : function(deep){
34103         if(this.el && this.isVisible()){
34104             this.fireEvent("beforehide", this);
34105             if(this.activeItem){
34106                 this.activeItem.deactivate();
34107                 this.activeItem = null;
34108             }
34109             this.el.hide();
34110             this.hidden = true;
34111             this.fireEvent("hide", this);
34112         }
34113         if(deep === true && this.parentMenu){
34114             this.parentMenu.hide(true);
34115         }
34116     },
34117
34118     /**
34119      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34120      * Any of the following are valid:
34121      * <ul>
34122      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34123      * <li>An HTMLElement object which will be converted to a menu item</li>
34124      * <li>A menu item config object that will be created as a new menu item</li>
34125      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34126      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34127      * </ul>
34128      * Usage:
34129      * <pre><code>
34130 // Create the menu
34131 var menu = new Roo.menu.Menu();
34132
34133 // Create a menu item to add by reference
34134 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34135
34136 // Add a bunch of items at once using different methods.
34137 // Only the last item added will be returned.
34138 var item = menu.add(
34139     menuItem,                // add existing item by ref
34140     'Dynamic Item',          // new TextItem
34141     '-',                     // new separator
34142     { text: 'Config Item' }  // new item by config
34143 );
34144 </code></pre>
34145      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34146      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34147      */
34148     add : function(){
34149         var a = arguments, l = a.length, item;
34150         for(var i = 0; i < l; i++){
34151             var el = a[i];
34152             if ((typeof(el) == "object") && el.xtype && el.xns) {
34153                 el = Roo.factory(el, Roo.menu);
34154             }
34155             
34156             if(el.render){ // some kind of Item
34157                 item = this.addItem(el);
34158             }else if(typeof el == "string"){ // string
34159                 if(el == "separator" || el == "-"){
34160                     item = this.addSeparator();
34161                 }else{
34162                     item = this.addText(el);
34163                 }
34164             }else if(el.tagName || el.el){ // element
34165                 item = this.addElement(el);
34166             }else if(typeof el == "object"){ // must be menu item config?
34167                 item = this.addMenuItem(el);
34168             }
34169         }
34170         return item;
34171     },
34172
34173     /**
34174      * Returns this menu's underlying {@link Roo.Element} object
34175      * @return {Roo.Element} The element
34176      */
34177     getEl : function(){
34178         if(!this.el){
34179             this.render();
34180         }
34181         return this.el;
34182     },
34183
34184     /**
34185      * Adds a separator bar to the menu
34186      * @return {Roo.menu.Item} The menu item that was added
34187      */
34188     addSeparator : function(){
34189         return this.addItem(new Roo.menu.Separator());
34190     },
34191
34192     /**
34193      * Adds an {@link Roo.Element} object to the menu
34194      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34195      * @return {Roo.menu.Item} The menu item that was added
34196      */
34197     addElement : function(el){
34198         return this.addItem(new Roo.menu.BaseItem(el));
34199     },
34200
34201     /**
34202      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34203      * @param {Roo.menu.Item} item The menu item to add
34204      * @return {Roo.menu.Item} The menu item that was added
34205      */
34206     addItem : function(item){
34207         this.items.add(item);
34208         if(this.ul){
34209             var li = document.createElement("li");
34210             li.className = "x-menu-list-item";
34211             this.ul.dom.appendChild(li);
34212             item.render(li, this);
34213             this.delayAutoWidth();
34214         }
34215         return item;
34216     },
34217
34218     /**
34219      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34220      * @param {Object} config A MenuItem config object
34221      * @return {Roo.menu.Item} The menu item that was added
34222      */
34223     addMenuItem : function(config){
34224         if(!(config instanceof Roo.menu.Item)){
34225             if(typeof config.checked == "boolean"){ // must be check menu item config?
34226                 config = new Roo.menu.CheckItem(config);
34227             }else{
34228                 config = new Roo.menu.Item(config);
34229             }
34230         }
34231         return this.addItem(config);
34232     },
34233
34234     /**
34235      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34236      * @param {String} text The text to display in the menu item
34237      * @return {Roo.menu.Item} The menu item that was added
34238      */
34239     addText : function(text){
34240         return this.addItem(new Roo.menu.TextItem({ text : text }));
34241     },
34242
34243     /**
34244      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34245      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34246      * @param {Roo.menu.Item} item The menu item to add
34247      * @return {Roo.menu.Item} The menu item that was added
34248      */
34249     insert : function(index, item){
34250         this.items.insert(index, item);
34251         if(this.ul){
34252             var li = document.createElement("li");
34253             li.className = "x-menu-list-item";
34254             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34255             item.render(li, this);
34256             this.delayAutoWidth();
34257         }
34258         return item;
34259     },
34260
34261     /**
34262      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34263      * @param {Roo.menu.Item} item The menu item to remove
34264      */
34265     remove : function(item){
34266         this.items.removeKey(item.id);
34267         item.destroy();
34268     },
34269
34270     /**
34271      * Removes and destroys all items in the menu
34272      */
34273     removeAll : function(){
34274         var f;
34275         while(f = this.items.first()){
34276             this.remove(f);
34277         }
34278     }
34279 });
34280
34281 // MenuNav is a private utility class used internally by the Menu
34282 Roo.menu.MenuNav = function(menu){
34283     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34284     this.scope = this.menu = menu;
34285 };
34286
34287 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34288     doRelay : function(e, h){
34289         var k = e.getKey();
34290         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34291             this.menu.tryActivate(0, 1);
34292             return false;
34293         }
34294         return h.call(this.scope || this, e, this.menu);
34295     },
34296
34297     up : function(e, m){
34298         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34299             m.tryActivate(m.items.length-1, -1);
34300         }
34301     },
34302
34303     down : function(e, m){
34304         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34305             m.tryActivate(0, 1);
34306         }
34307     },
34308
34309     right : function(e, m){
34310         if(m.activeItem){
34311             m.activeItem.expandMenu(true);
34312         }
34313     },
34314
34315     left : function(e, m){
34316         m.hide();
34317         if(m.parentMenu && m.parentMenu.activeItem){
34318             m.parentMenu.activeItem.activate();
34319         }
34320     },
34321
34322     enter : function(e, m){
34323         if(m.activeItem){
34324             e.stopPropagation();
34325             m.activeItem.onClick(e);
34326             m.fireEvent("click", this, m.activeItem);
34327             return true;
34328         }
34329     }
34330 });/*
34331  * Based on:
34332  * Ext JS Library 1.1.1
34333  * Copyright(c) 2006-2007, Ext JS, LLC.
34334  *
34335  * Originally Released Under LGPL - original licence link has changed is not relivant.
34336  *
34337  * Fork - LGPL
34338  * <script type="text/javascript">
34339  */
34340  
34341 /**
34342  * @class Roo.menu.MenuMgr
34343  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34344  * @singleton
34345  */
34346 Roo.menu.MenuMgr = function(){
34347    var menus, active, groups = {}, attached = false, lastShow = new Date();
34348
34349    // private - called when first menu is created
34350    function init(){
34351        menus = {};
34352        active = new Roo.util.MixedCollection();
34353        Roo.get(document).addKeyListener(27, function(){
34354            if(active.length > 0){
34355                hideAll();
34356            }
34357        });
34358    }
34359
34360    // private
34361    function hideAll(){
34362        if(active && active.length > 0){
34363            var c = active.clone();
34364            c.each(function(m){
34365                m.hide();
34366            });
34367        }
34368    }
34369
34370    // private
34371    function onHide(m){
34372        active.remove(m);
34373        if(active.length < 1){
34374            Roo.get(document).un("mousedown", onMouseDown);
34375            attached = false;
34376        }
34377    }
34378
34379    // private
34380    function onShow(m){
34381        var last = active.last();
34382        lastShow = new Date();
34383        active.add(m);
34384        if(!attached){
34385            Roo.get(document).on("mousedown", onMouseDown);
34386            attached = true;
34387        }
34388        if(m.parentMenu){
34389           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34390           m.parentMenu.activeChild = m;
34391        }else if(last && last.isVisible()){
34392           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34393        }
34394    }
34395
34396    // private
34397    function onBeforeHide(m){
34398        if(m.activeChild){
34399            m.activeChild.hide();
34400        }
34401        if(m.autoHideTimer){
34402            clearTimeout(m.autoHideTimer);
34403            delete m.autoHideTimer;
34404        }
34405    }
34406
34407    // private
34408    function onBeforeShow(m){
34409        var pm = m.parentMenu;
34410        if(!pm && !m.allowOtherMenus){
34411            hideAll();
34412        }else if(pm && pm.activeChild && active != m){
34413            pm.activeChild.hide();
34414        }
34415    }
34416
34417    // private
34418    function onMouseDown(e){
34419        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34420            hideAll();
34421        }
34422    }
34423
34424    // private
34425    function onBeforeCheck(mi, state){
34426        if(state){
34427            var g = groups[mi.group];
34428            for(var i = 0, l = g.length; i < l; i++){
34429                if(g[i] != mi){
34430                    g[i].setChecked(false);
34431                }
34432            }
34433        }
34434    }
34435
34436    return {
34437
34438        /**
34439         * Hides all menus that are currently visible
34440         */
34441        hideAll : function(){
34442             hideAll();  
34443        },
34444
34445        // private
34446        register : function(menu){
34447            if(!menus){
34448                init();
34449            }
34450            menus[menu.id] = menu;
34451            menu.on("beforehide", onBeforeHide);
34452            menu.on("hide", onHide);
34453            menu.on("beforeshow", onBeforeShow);
34454            menu.on("show", onShow);
34455            var g = menu.group;
34456            if(g && menu.events["checkchange"]){
34457                if(!groups[g]){
34458                    groups[g] = [];
34459                }
34460                groups[g].push(menu);
34461                menu.on("checkchange", onCheck);
34462            }
34463        },
34464
34465         /**
34466          * Returns a {@link Roo.menu.Menu} object
34467          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34468          * be used to generate and return a new Menu instance.
34469          */
34470        get : function(menu){
34471            if(typeof menu == "string"){ // menu id
34472                return menus[menu];
34473            }else if(menu.events){  // menu instance
34474                return menu;
34475            }else if(typeof menu.length == 'number'){ // array of menu items?
34476                return new Roo.menu.Menu({items:menu});
34477            }else{ // otherwise, must be a config
34478                return new Roo.menu.Menu(menu);
34479            }
34480        },
34481
34482        // private
34483        unregister : function(menu){
34484            delete menus[menu.id];
34485            menu.un("beforehide", onBeforeHide);
34486            menu.un("hide", onHide);
34487            menu.un("beforeshow", onBeforeShow);
34488            menu.un("show", onShow);
34489            var g = menu.group;
34490            if(g && menu.events["checkchange"]){
34491                groups[g].remove(menu);
34492                menu.un("checkchange", onCheck);
34493            }
34494        },
34495
34496        // private
34497        registerCheckable : function(menuItem){
34498            var g = menuItem.group;
34499            if(g){
34500                if(!groups[g]){
34501                    groups[g] = [];
34502                }
34503                groups[g].push(menuItem);
34504                menuItem.on("beforecheckchange", onBeforeCheck);
34505            }
34506        },
34507
34508        // private
34509        unregisterCheckable : function(menuItem){
34510            var g = menuItem.group;
34511            if(g){
34512                groups[g].remove(menuItem);
34513                menuItem.un("beforecheckchange", onBeforeCheck);
34514            }
34515        }
34516    };
34517 }();/*
34518  * Based on:
34519  * Ext JS Library 1.1.1
34520  * Copyright(c) 2006-2007, Ext JS, LLC.
34521  *
34522  * Originally Released Under LGPL - original licence link has changed is not relivant.
34523  *
34524  * Fork - LGPL
34525  * <script type="text/javascript">
34526  */
34527  
34528
34529 /**
34530  * @class Roo.menu.BaseItem
34531  * @extends Roo.Component
34532  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34533  * management and base configuration options shared by all menu components.
34534  * @constructor
34535  * Creates a new BaseItem
34536  * @param {Object} config Configuration options
34537  */
34538 Roo.menu.BaseItem = function(config){
34539     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34540
34541     this.addEvents({
34542         /**
34543          * @event click
34544          * Fires when this item is clicked
34545          * @param {Roo.menu.BaseItem} this
34546          * @param {Roo.EventObject} e
34547          */
34548         click: true,
34549         /**
34550          * @event activate
34551          * Fires when this item is activated
34552          * @param {Roo.menu.BaseItem} this
34553          */
34554         activate : true,
34555         /**
34556          * @event deactivate
34557          * Fires when this item is deactivated
34558          * @param {Roo.menu.BaseItem} this
34559          */
34560         deactivate : true
34561     });
34562
34563     if(this.handler){
34564         this.on("click", this.handler, this.scope, true);
34565     }
34566 };
34567
34568 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34569     /**
34570      * @cfg {Function} handler
34571      * A function that will handle the click event of this menu item (defaults to undefined)
34572      */
34573     /**
34574      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34575      */
34576     canActivate : false,
34577     /**
34578      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34579      */
34580     activeClass : "x-menu-item-active",
34581     /**
34582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34583      */
34584     hideOnClick : true,
34585     /**
34586      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34587      */
34588     hideDelay : 100,
34589
34590     // private
34591     ctype: "Roo.menu.BaseItem",
34592
34593     // private
34594     actionMode : "container",
34595
34596     // private
34597     render : function(container, parentMenu){
34598         this.parentMenu = parentMenu;
34599         Roo.menu.BaseItem.superclass.render.call(this, container);
34600         this.container.menuItemId = this.id;
34601     },
34602
34603     // private
34604     onRender : function(container, position){
34605         this.el = Roo.get(this.el);
34606         container.dom.appendChild(this.el.dom);
34607     },
34608
34609     // private
34610     onClick : function(e){
34611         if(!this.disabled && this.fireEvent("click", this, e) !== false
34612                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34613             this.handleClick(e);
34614         }else{
34615             e.stopEvent();
34616         }
34617     },
34618
34619     // private
34620     activate : function(){
34621         if(this.disabled){
34622             return false;
34623         }
34624         var li = this.container;
34625         li.addClass(this.activeClass);
34626         this.region = li.getRegion().adjust(2, 2, -2, -2);
34627         this.fireEvent("activate", this);
34628         return true;
34629     },
34630
34631     // private
34632     deactivate : function(){
34633         this.container.removeClass(this.activeClass);
34634         this.fireEvent("deactivate", this);
34635     },
34636
34637     // private
34638     shouldDeactivate : function(e){
34639         return !this.region || !this.region.contains(e.getPoint());
34640     },
34641
34642     // private
34643     handleClick : function(e){
34644         if(this.hideOnClick){
34645             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34646         }
34647     },
34648
34649     // private
34650     expandMenu : function(autoActivate){
34651         // do nothing
34652     },
34653
34654     // private
34655     hideMenu : function(){
34656         // do nothing
34657     }
34658 });/*
34659  * Based on:
34660  * Ext JS Library 1.1.1
34661  * Copyright(c) 2006-2007, Ext JS, LLC.
34662  *
34663  * Originally Released Under LGPL - original licence link has changed is not relivant.
34664  *
34665  * Fork - LGPL
34666  * <script type="text/javascript">
34667  */
34668  
34669 /**
34670  * @class Roo.menu.Adapter
34671  * @extends Roo.menu.BaseItem
34672  * 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.
34673  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34674  * @constructor
34675  * Creates a new Adapter
34676  * @param {Object} config Configuration options
34677  */
34678 Roo.menu.Adapter = function(component, config){
34679     Roo.menu.Adapter.superclass.constructor.call(this, config);
34680     this.component = component;
34681 };
34682 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34683     // private
34684     canActivate : true,
34685
34686     // private
34687     onRender : function(container, position){
34688         this.component.render(container);
34689         this.el = this.component.getEl();
34690     },
34691
34692     // private
34693     activate : function(){
34694         if(this.disabled){
34695             return false;
34696         }
34697         this.component.focus();
34698         this.fireEvent("activate", this);
34699         return true;
34700     },
34701
34702     // private
34703     deactivate : function(){
34704         this.fireEvent("deactivate", this);
34705     },
34706
34707     // private
34708     disable : function(){
34709         this.component.disable();
34710         Roo.menu.Adapter.superclass.disable.call(this);
34711     },
34712
34713     // private
34714     enable : function(){
34715         this.component.enable();
34716         Roo.menu.Adapter.superclass.enable.call(this);
34717     }
34718 });/*
34719  * Based on:
34720  * Ext JS Library 1.1.1
34721  * Copyright(c) 2006-2007, Ext JS, LLC.
34722  *
34723  * Originally Released Under LGPL - original licence link has changed is not relivant.
34724  *
34725  * Fork - LGPL
34726  * <script type="text/javascript">
34727  */
34728
34729 /**
34730  * @class Roo.menu.TextItem
34731  * @extends Roo.menu.BaseItem
34732  * Adds a static text string to a menu, usually used as either a heading or group separator.
34733  * Note: old style constructor with text is still supported.
34734  * 
34735  * @constructor
34736  * Creates a new TextItem
34737  * @param {Object} cfg Configuration
34738  */
34739 Roo.menu.TextItem = function(cfg){
34740     if (typeof(cfg) == 'string') {
34741         this.text = cfg;
34742     } else {
34743         Roo.apply(this,cfg);
34744     }
34745     
34746     Roo.menu.TextItem.superclass.constructor.call(this);
34747 };
34748
34749 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34750     /**
34751      * @cfg {Boolean} text Text to show on item.
34752      */
34753     text : '',
34754     
34755     /**
34756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34757      */
34758     hideOnClick : false,
34759     /**
34760      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34761      */
34762     itemCls : "x-menu-text",
34763
34764     // private
34765     onRender : function(){
34766         var s = document.createElement("span");
34767         s.className = this.itemCls;
34768         s.innerHTML = this.text;
34769         this.el = s;
34770         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34771     }
34772 });/*
34773  * Based on:
34774  * Ext JS Library 1.1.1
34775  * Copyright(c) 2006-2007, Ext JS, LLC.
34776  *
34777  * Originally Released Under LGPL - original licence link has changed is not relivant.
34778  *
34779  * Fork - LGPL
34780  * <script type="text/javascript">
34781  */
34782
34783 /**
34784  * @class Roo.menu.Separator
34785  * @extends Roo.menu.BaseItem
34786  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34787  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34788  * @constructor
34789  * @param {Object} config Configuration options
34790  */
34791 Roo.menu.Separator = function(config){
34792     Roo.menu.Separator.superclass.constructor.call(this, config);
34793 };
34794
34795 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34796     /**
34797      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34798      */
34799     itemCls : "x-menu-sep",
34800     /**
34801      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34802      */
34803     hideOnClick : false,
34804
34805     // private
34806     onRender : function(li){
34807         var s = document.createElement("span");
34808         s.className = this.itemCls;
34809         s.innerHTML = "&#160;";
34810         this.el = s;
34811         li.addClass("x-menu-sep-li");
34812         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34813     }
34814 });/*
34815  * Based on:
34816  * Ext JS Library 1.1.1
34817  * Copyright(c) 2006-2007, Ext JS, LLC.
34818  *
34819  * Originally Released Under LGPL - original licence link has changed is not relivant.
34820  *
34821  * Fork - LGPL
34822  * <script type="text/javascript">
34823  */
34824 /**
34825  * @class Roo.menu.Item
34826  * @extends Roo.menu.BaseItem
34827  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34828  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34829  * activation and click handling.
34830  * @constructor
34831  * Creates a new Item
34832  * @param {Object} config Configuration options
34833  */
34834 Roo.menu.Item = function(config){
34835     Roo.menu.Item.superclass.constructor.call(this, config);
34836     if(this.menu){
34837         this.menu = Roo.menu.MenuMgr.get(this.menu);
34838     }
34839 };
34840 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34841     
34842     /**
34843      * @cfg {String} text
34844      * The text to show on the menu item.
34845      */
34846     text: '',
34847      /**
34848      * @cfg {String} HTML to render in menu
34849      * The text to show on the menu item (HTML version).
34850      */
34851     html: '',
34852     /**
34853      * @cfg {String} icon
34854      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34855      */
34856     icon: undefined,
34857     /**
34858      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34859      */
34860     itemCls : "x-menu-item",
34861     /**
34862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34863      */
34864     canActivate : true,
34865     /**
34866      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34867      */
34868     showDelay: 200,
34869     // doc'd in BaseItem
34870     hideDelay: 200,
34871
34872     // private
34873     ctype: "Roo.menu.Item",
34874     
34875     // private
34876     onRender : function(container, position){
34877         var el = document.createElement("a");
34878         el.hideFocus = true;
34879         el.unselectable = "on";
34880         el.href = this.href || "#";
34881         if(this.hrefTarget){
34882             el.target = this.hrefTarget;
34883         }
34884         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34885         
34886         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34887         
34888         el.innerHTML = String.format(
34889                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34890                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34891         this.el = el;
34892         Roo.menu.Item.superclass.onRender.call(this, container, position);
34893     },
34894
34895     /**
34896      * Sets the text to display in this menu item
34897      * @param {String} text The text to display
34898      * @param {Boolean} isHTML true to indicate text is pure html.
34899      */
34900     setText : function(text, isHTML){
34901         if (isHTML) {
34902             this.html = text;
34903         } else {
34904             this.text = text;
34905             this.html = '';
34906         }
34907         if(this.rendered){
34908             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34909      
34910             this.el.update(String.format(
34911                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34912                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34913             this.parentMenu.autoWidth();
34914         }
34915     },
34916
34917     // private
34918     handleClick : function(e){
34919         if(!this.href){ // if no link defined, stop the event automatically
34920             e.stopEvent();
34921         }
34922         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34923     },
34924
34925     // private
34926     activate : function(autoExpand){
34927         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34928             this.focus();
34929             if(autoExpand){
34930                 this.expandMenu();
34931             }
34932         }
34933         return true;
34934     },
34935
34936     // private
34937     shouldDeactivate : function(e){
34938         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34939             if(this.menu && this.menu.isVisible()){
34940                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34941             }
34942             return true;
34943         }
34944         return false;
34945     },
34946
34947     // private
34948     deactivate : function(){
34949         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34950         this.hideMenu();
34951     },
34952
34953     // private
34954     expandMenu : function(autoActivate){
34955         if(!this.disabled && this.menu){
34956             clearTimeout(this.hideTimer);
34957             delete this.hideTimer;
34958             if(!this.menu.isVisible() && !this.showTimer){
34959                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34960             }else if (this.menu.isVisible() && autoActivate){
34961                 this.menu.tryActivate(0, 1);
34962             }
34963         }
34964     },
34965
34966     // private
34967     deferExpand : function(autoActivate){
34968         delete this.showTimer;
34969         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34970         if(autoActivate){
34971             this.menu.tryActivate(0, 1);
34972         }
34973     },
34974
34975     // private
34976     hideMenu : function(){
34977         clearTimeout(this.showTimer);
34978         delete this.showTimer;
34979         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34980             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34981         }
34982     },
34983
34984     // private
34985     deferHide : function(){
34986         delete this.hideTimer;
34987         this.menu.hide();
34988     }
34989 });/*
34990  * Based on:
34991  * Ext JS Library 1.1.1
34992  * Copyright(c) 2006-2007, Ext JS, LLC.
34993  *
34994  * Originally Released Under LGPL - original licence link has changed is not relivant.
34995  *
34996  * Fork - LGPL
34997  * <script type="text/javascript">
34998  */
34999  
35000 /**
35001  * @class Roo.menu.CheckItem
35002  * @extends Roo.menu.Item
35003  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35004  * @constructor
35005  * Creates a new CheckItem
35006  * @param {Object} config Configuration options
35007  */
35008 Roo.menu.CheckItem = function(config){
35009     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35010     this.addEvents({
35011         /**
35012          * @event beforecheckchange
35013          * Fires before the checked value is set, providing an opportunity to cancel if needed
35014          * @param {Roo.menu.CheckItem} this
35015          * @param {Boolean} checked The new checked value that will be set
35016          */
35017         "beforecheckchange" : true,
35018         /**
35019          * @event checkchange
35020          * Fires after the checked value has been set
35021          * @param {Roo.menu.CheckItem} this
35022          * @param {Boolean} checked The checked value that was set
35023          */
35024         "checkchange" : true
35025     });
35026     if(this.checkHandler){
35027         this.on('checkchange', this.checkHandler, this.scope);
35028     }
35029 };
35030 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35031     /**
35032      * @cfg {String} group
35033      * All check items with the same group name will automatically be grouped into a single-select
35034      * radio button group (defaults to '')
35035      */
35036     /**
35037      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35038      */
35039     itemCls : "x-menu-item x-menu-check-item",
35040     /**
35041      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35042      */
35043     groupClass : "x-menu-group-item",
35044
35045     /**
35046      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35047      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35048      * initialized with checked = true will be rendered as checked.
35049      */
35050     checked: false,
35051
35052     // private
35053     ctype: "Roo.menu.CheckItem",
35054
35055     // private
35056     onRender : function(c){
35057         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35058         if(this.group){
35059             this.el.addClass(this.groupClass);
35060         }
35061         Roo.menu.MenuMgr.registerCheckable(this);
35062         if(this.checked){
35063             this.checked = false;
35064             this.setChecked(true, true);
35065         }
35066     },
35067
35068     // private
35069     destroy : function(){
35070         if(this.rendered){
35071             Roo.menu.MenuMgr.unregisterCheckable(this);
35072         }
35073         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35074     },
35075
35076     /**
35077      * Set the checked state of this item
35078      * @param {Boolean} checked The new checked value
35079      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35080      */
35081     setChecked : function(state, suppressEvent){
35082         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35083             if(this.container){
35084                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35085             }
35086             this.checked = state;
35087             if(suppressEvent !== true){
35088                 this.fireEvent("checkchange", this, state);
35089             }
35090         }
35091     },
35092
35093     // private
35094     handleClick : function(e){
35095        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35096            this.setChecked(!this.checked);
35097        }
35098        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35099     }
35100 });/*
35101  * Based on:
35102  * Ext JS Library 1.1.1
35103  * Copyright(c) 2006-2007, Ext JS, LLC.
35104  *
35105  * Originally Released Under LGPL - original licence link has changed is not relivant.
35106  *
35107  * Fork - LGPL
35108  * <script type="text/javascript">
35109  */
35110  
35111 /**
35112  * @class Roo.menu.DateItem
35113  * @extends Roo.menu.Adapter
35114  * A menu item that wraps the {@link Roo.DatPicker} component.
35115  * @constructor
35116  * Creates a new DateItem
35117  * @param {Object} config Configuration options
35118  */
35119 Roo.menu.DateItem = function(config){
35120     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35121     /** The Roo.DatePicker object @type Roo.DatePicker */
35122     this.picker = this.component;
35123     this.addEvents({select: true});
35124     
35125     this.picker.on("render", function(picker){
35126         picker.getEl().swallowEvent("click");
35127         picker.container.addClass("x-menu-date-item");
35128     });
35129
35130     this.picker.on("select", this.onSelect, this);
35131 };
35132
35133 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35134     // private
35135     onSelect : function(picker, date){
35136         this.fireEvent("select", this, date, picker);
35137         Roo.menu.DateItem.superclass.handleClick.call(this);
35138     }
35139 });/*
35140  * Based on:
35141  * Ext JS Library 1.1.1
35142  * Copyright(c) 2006-2007, Ext JS, LLC.
35143  *
35144  * Originally Released Under LGPL - original licence link has changed is not relivant.
35145  *
35146  * Fork - LGPL
35147  * <script type="text/javascript">
35148  */
35149  
35150 /**
35151  * @class Roo.menu.ColorItem
35152  * @extends Roo.menu.Adapter
35153  * A menu item that wraps the {@link Roo.ColorPalette} component.
35154  * @constructor
35155  * Creates a new ColorItem
35156  * @param {Object} config Configuration options
35157  */
35158 Roo.menu.ColorItem = function(config){
35159     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35160     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35161     this.palette = this.component;
35162     this.relayEvents(this.palette, ["select"]);
35163     if(this.selectHandler){
35164         this.on('select', this.selectHandler, this.scope);
35165     }
35166 };
35167 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35168  * Based on:
35169  * Ext JS Library 1.1.1
35170  * Copyright(c) 2006-2007, Ext JS, LLC.
35171  *
35172  * Originally Released Under LGPL - original licence link has changed is not relivant.
35173  *
35174  * Fork - LGPL
35175  * <script type="text/javascript">
35176  */
35177  
35178
35179 /**
35180  * @class Roo.menu.DateMenu
35181  * @extends Roo.menu.Menu
35182  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35183  * @constructor
35184  * Creates a new DateMenu
35185  * @param {Object} config Configuration options
35186  */
35187 Roo.menu.DateMenu = function(config){
35188     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35189     this.plain = true;
35190     var di = new Roo.menu.DateItem(config);
35191     this.add(di);
35192     /**
35193      * The {@link Roo.DatePicker} instance for this DateMenu
35194      * @type DatePicker
35195      */
35196     this.picker = di.picker;
35197     /**
35198      * @event select
35199      * @param {DatePicker} picker
35200      * @param {Date} date
35201      */
35202     this.relayEvents(di, ["select"]);
35203
35204     this.on('beforeshow', function(){
35205         if(this.picker){
35206             this.picker.hideMonthPicker(true);
35207         }
35208     }, this);
35209 };
35210 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35211     cls:'x-date-menu'
35212 });/*
35213  * Based on:
35214  * Ext JS Library 1.1.1
35215  * Copyright(c) 2006-2007, Ext JS, LLC.
35216  *
35217  * Originally Released Under LGPL - original licence link has changed is not relivant.
35218  *
35219  * Fork - LGPL
35220  * <script type="text/javascript">
35221  */
35222  
35223
35224 /**
35225  * @class Roo.menu.ColorMenu
35226  * @extends Roo.menu.Menu
35227  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35228  * @constructor
35229  * Creates a new ColorMenu
35230  * @param {Object} config Configuration options
35231  */
35232 Roo.menu.ColorMenu = function(config){
35233     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35234     this.plain = true;
35235     var ci = new Roo.menu.ColorItem(config);
35236     this.add(ci);
35237     /**
35238      * The {@link Roo.ColorPalette} instance for this ColorMenu
35239      * @type ColorPalette
35240      */
35241     this.palette = ci.palette;
35242     /**
35243      * @event select
35244      * @param {ColorPalette} palette
35245      * @param {String} color
35246      */
35247     this.relayEvents(ci, ["select"]);
35248 };
35249 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35250  * Based on:
35251  * Ext JS Library 1.1.1
35252  * Copyright(c) 2006-2007, Ext JS, LLC.
35253  *
35254  * Originally Released Under LGPL - original licence link has changed is not relivant.
35255  *
35256  * Fork - LGPL
35257  * <script type="text/javascript">
35258  */
35259  
35260 /**
35261  * @class Roo.form.Field
35262  * @extends Roo.BoxComponent
35263  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35264  * @constructor
35265  * Creates a new Field
35266  * @param {Object} config Configuration options
35267  */
35268 Roo.form.Field = function(config){
35269     Roo.form.Field.superclass.constructor.call(this, config);
35270 };
35271
35272 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35273     /**
35274      * @cfg {String} fieldLabel Label to use when rendering a form.
35275      */
35276        /**
35277      * @cfg {String} qtip Mouse over tip
35278      */
35279      
35280     /**
35281      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35282      */
35283     invalidClass : "x-form-invalid",
35284     /**
35285      * @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")
35286      */
35287     invalidText : "The value in this field is invalid",
35288     /**
35289      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35290      */
35291     focusClass : "x-form-focus",
35292     /**
35293      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35294       automatic validation (defaults to "keyup").
35295      */
35296     validationEvent : "keyup",
35297     /**
35298      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35299      */
35300     validateOnBlur : true,
35301     /**
35302      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35303      */
35304     validationDelay : 250,
35305     /**
35306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35307      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35308      */
35309     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35310     /**
35311      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35312      */
35313     fieldClass : "x-form-field",
35314     /**
35315      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35316      *<pre>
35317 Value         Description
35318 -----------   ----------------------------------------------------------------------
35319 qtip          Display a quick tip when the user hovers over the field
35320 title         Display a default browser title attribute popup
35321 under         Add a block div beneath the field containing the error text
35322 side          Add an error icon to the right of the field with a popup on hover
35323 [element id]  Add the error text directly to the innerHTML of the specified element
35324 </pre>
35325      */
35326     msgTarget : 'qtip',
35327     /**
35328      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35329      */
35330     msgFx : 'normal',
35331
35332     /**
35333      * @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.
35334      */
35335     readOnly : false,
35336
35337     /**
35338      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35339      */
35340     disabled : false,
35341
35342     /**
35343      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35344      */
35345     inputType : undefined,
35346     
35347     /**
35348      * @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).
35349          */
35350         tabIndex : undefined,
35351         
35352     // private
35353     isFormField : true,
35354
35355     // private
35356     hasFocus : false,
35357     /**
35358      * @property {Roo.Element} fieldEl
35359      * Element Containing the rendered Field (with label etc.)
35360      */
35361     /**
35362      * @cfg {Mixed} value A value to initialize this field with.
35363      */
35364     value : undefined,
35365
35366     /**
35367      * @cfg {String} name The field's HTML name attribute.
35368      */
35369     /**
35370      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35371      */
35372
35373         // private ??
35374         initComponent : function(){
35375         Roo.form.Field.superclass.initComponent.call(this);
35376         this.addEvents({
35377             /**
35378              * @event focus
35379              * Fires when this field receives input focus.
35380              * @param {Roo.form.Field} this
35381              */
35382             focus : true,
35383             /**
35384              * @event blur
35385              * Fires when this field loses input focus.
35386              * @param {Roo.form.Field} this
35387              */
35388             blur : true,
35389             /**
35390              * @event specialkey
35391              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35392              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35393              * @param {Roo.form.Field} this
35394              * @param {Roo.EventObject} e The event object
35395              */
35396             specialkey : true,
35397             /**
35398              * @event change
35399              * Fires just before the field blurs if the field value has changed.
35400              * @param {Roo.form.Field} this
35401              * @param {Mixed} newValue The new value
35402              * @param {Mixed} oldValue The original value
35403              */
35404             change : true,
35405             /**
35406              * @event invalid
35407              * Fires after the field has been marked as invalid.
35408              * @param {Roo.form.Field} this
35409              * @param {String} msg The validation message
35410              */
35411             invalid : true,
35412             /**
35413              * @event valid
35414              * Fires after the field has been validated with no errors.
35415              * @param {Roo.form.Field} this
35416              */
35417             valid : true,
35418              /**
35419              * @event keyup
35420              * Fires after the key up
35421              * @param {Roo.form.Field} this
35422              * @param {Roo.EventObject}  e The event Object
35423              */
35424             keyup : true
35425         });
35426     },
35427
35428     /**
35429      * Returns the name attribute of the field if available
35430      * @return {String} name The field name
35431      */
35432     getName: function(){
35433          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35434     },
35435
35436     // private
35437     onRender : function(ct, position){
35438         Roo.form.Field.superclass.onRender.call(this, ct, position);
35439         if(!this.el){
35440             var cfg = this.getAutoCreate();
35441             if(!cfg.name){
35442                 cfg.name = this.name || this.id;
35443             }
35444             if(this.inputType){
35445                 cfg.type = this.inputType;
35446             }
35447             this.el = ct.createChild(cfg, position);
35448         }
35449         var type = this.el.dom.type;
35450         if(type){
35451             if(type == 'password'){
35452                 type = 'text';
35453             }
35454             this.el.addClass('x-form-'+type);
35455         }
35456         if(this.readOnly){
35457             this.el.dom.readOnly = true;
35458         }
35459         if(this.tabIndex !== undefined){
35460             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35461         }
35462
35463         this.el.addClass([this.fieldClass, this.cls]);
35464         this.initValue();
35465     },
35466
35467     /**
35468      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35469      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35470      * @return {Roo.form.Field} this
35471      */
35472     applyTo : function(target){
35473         this.allowDomMove = false;
35474         this.el = Roo.get(target);
35475         this.render(this.el.dom.parentNode);
35476         return this;
35477     },
35478
35479     // private
35480     initValue : function(){
35481         if(this.value !== undefined){
35482             this.setValue(this.value);
35483         }else if(this.el.dom.value.length > 0){
35484             this.setValue(this.el.dom.value);
35485         }
35486     },
35487
35488     /**
35489      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35490      */
35491     isDirty : function() {
35492         if(this.disabled) {
35493             return false;
35494         }
35495         return String(this.getValue()) !== String(this.originalValue);
35496     },
35497
35498     // private
35499     afterRender : function(){
35500         Roo.form.Field.superclass.afterRender.call(this);
35501         this.initEvents();
35502     },
35503
35504     // private
35505     fireKey : function(e){
35506         //Roo.log('field ' + e.getKey());
35507         if(e.isNavKeyPress()){
35508             this.fireEvent("specialkey", this, e);
35509         }
35510     },
35511
35512     /**
35513      * Resets the current field value to the originally loaded value and clears any validation messages
35514      */
35515     reset : function(){
35516         this.setValue(this.originalValue);
35517         this.clearInvalid();
35518     },
35519
35520     // private
35521     initEvents : function(){
35522         // safari killled keypress - so keydown is now used..
35523         this.el.on("keydown" , this.fireKey,  this);
35524         this.el.on("focus", this.onFocus,  this);
35525         this.el.on("blur", this.onBlur,  this);
35526         this.el.relayEvent('keyup', this);
35527
35528         // reference to original value for reset
35529         this.originalValue = this.getValue();
35530     },
35531
35532     // private
35533     onFocus : function(){
35534         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35535             this.el.addClass(this.focusClass);
35536         }
35537         if(!this.hasFocus){
35538             this.hasFocus = true;
35539             this.startValue = this.getValue();
35540             this.fireEvent("focus", this);
35541         }
35542     },
35543
35544     beforeBlur : Roo.emptyFn,
35545
35546     // private
35547     onBlur : function(){
35548         this.beforeBlur();
35549         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35550             this.el.removeClass(this.focusClass);
35551         }
35552         this.hasFocus = false;
35553         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35554             this.validate();
35555         }
35556         var v = this.getValue();
35557         if(String(v) !== String(this.startValue)){
35558             this.fireEvent('change', this, v, this.startValue);
35559         }
35560         this.fireEvent("blur", this);
35561     },
35562
35563     /**
35564      * Returns whether or not the field value is currently valid
35565      * @param {Boolean} preventMark True to disable marking the field invalid
35566      * @return {Boolean} True if the value is valid, else false
35567      */
35568     isValid : function(preventMark){
35569         if(this.disabled){
35570             return true;
35571         }
35572         var restore = this.preventMark;
35573         this.preventMark = preventMark === true;
35574         var v = this.validateValue(this.processValue(this.getRawValue()));
35575         this.preventMark = restore;
35576         return v;
35577     },
35578
35579     /**
35580      * Validates the field value
35581      * @return {Boolean} True if the value is valid, else false
35582      */
35583     validate : function(){
35584         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35585             this.clearInvalid();
35586             return true;
35587         }
35588         return false;
35589     },
35590
35591     processValue : function(value){
35592         return value;
35593     },
35594
35595     // private
35596     // Subclasses should provide the validation implementation by overriding this
35597     validateValue : function(value){
35598         return true;
35599     },
35600
35601     /**
35602      * Mark this field as invalid
35603      * @param {String} msg The validation message
35604      */
35605     markInvalid : function(msg){
35606         if(!this.rendered || this.preventMark){ // not rendered
35607             return;
35608         }
35609         this.el.addClass(this.invalidClass);
35610         msg = msg || this.invalidText;
35611         switch(this.msgTarget){
35612             case 'qtip':
35613                 this.el.dom.qtip = msg;
35614                 this.el.dom.qclass = 'x-form-invalid-tip';
35615                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35616                     Roo.QuickTips.enable();
35617                 }
35618                 break;
35619             case 'title':
35620                 this.el.dom.title = msg;
35621                 break;
35622             case 'under':
35623                 if(!this.errorEl){
35624                     var elp = this.el.findParent('.x-form-element', 5, true);
35625                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35626                     this.errorEl.setWidth(elp.getWidth(true)-20);
35627                 }
35628                 this.errorEl.update(msg);
35629                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35630                 break;
35631             case 'side':
35632                 if(!this.errorIcon){
35633                     var elp = this.el.findParent('.x-form-element', 5, true);
35634                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35635                 }
35636                 this.alignErrorIcon();
35637                 this.errorIcon.dom.qtip = msg;
35638                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35639                 this.errorIcon.show();
35640                 this.on('resize', this.alignErrorIcon, this);
35641                 break;
35642             default:
35643                 var t = Roo.getDom(this.msgTarget);
35644                 t.innerHTML = msg;
35645                 t.style.display = this.msgDisplay;
35646                 break;
35647         }
35648         this.fireEvent('invalid', this, msg);
35649     },
35650
35651     // private
35652     alignErrorIcon : function(){
35653         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35654     },
35655
35656     /**
35657      * Clear any invalid styles/messages for this field
35658      */
35659     clearInvalid : function(){
35660         if(!this.rendered || this.preventMark){ // not rendered
35661             return;
35662         }
35663         this.el.removeClass(this.invalidClass);
35664         switch(this.msgTarget){
35665             case 'qtip':
35666                 this.el.dom.qtip = '';
35667                 break;
35668             case 'title':
35669                 this.el.dom.title = '';
35670                 break;
35671             case 'under':
35672                 if(this.errorEl){
35673                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35674                 }
35675                 break;
35676             case 'side':
35677                 if(this.errorIcon){
35678                     this.errorIcon.dom.qtip = '';
35679                     this.errorIcon.hide();
35680                     this.un('resize', this.alignErrorIcon, this);
35681                 }
35682                 break;
35683             default:
35684                 var t = Roo.getDom(this.msgTarget);
35685                 t.innerHTML = '';
35686                 t.style.display = 'none';
35687                 break;
35688         }
35689         this.fireEvent('valid', this);
35690     },
35691
35692     /**
35693      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35694      * @return {Mixed} value The field value
35695      */
35696     getRawValue : function(){
35697         var v = this.el.getValue();
35698         if(v === this.emptyText){
35699             v = '';
35700         }
35701         return v;
35702     },
35703
35704     /**
35705      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35706      * @return {Mixed} value The field value
35707      */
35708     getValue : function(){
35709         var v = this.el.getValue();
35710         if(v === this.emptyText || v === undefined){
35711             v = '';
35712         }
35713         return v;
35714     },
35715
35716     /**
35717      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35718      * @param {Mixed} value The value to set
35719      */
35720     setRawValue : function(v){
35721         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35722     },
35723
35724     /**
35725      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35726      * @param {Mixed} value The value to set
35727      */
35728     setValue : function(v){
35729         this.value = v;
35730         if(this.rendered){
35731             this.el.dom.value = (v === null || v === undefined ? '' : v);
35732             this.validate();
35733         }
35734     },
35735
35736     adjustSize : function(w, h){
35737         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35738         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35739         return s;
35740     },
35741
35742     adjustWidth : function(tag, w){
35743         tag = tag.toLowerCase();
35744         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35745             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35746                 if(tag == 'input'){
35747                     return w + 2;
35748                 }
35749                 if(tag = 'textarea'){
35750                     return w-2;
35751                 }
35752             }else if(Roo.isOpera){
35753                 if(tag == 'input'){
35754                     return w + 2;
35755                 }
35756                 if(tag = 'textarea'){
35757                     return w-2;
35758                 }
35759             }
35760         }
35761         return w;
35762     }
35763 });
35764
35765
35766 // anything other than normal should be considered experimental
35767 Roo.form.Field.msgFx = {
35768     normal : {
35769         show: function(msgEl, f){
35770             msgEl.setDisplayed('block');
35771         },
35772
35773         hide : function(msgEl, f){
35774             msgEl.setDisplayed(false).update('');
35775         }
35776     },
35777
35778     slide : {
35779         show: function(msgEl, f){
35780             msgEl.slideIn('t', {stopFx:true});
35781         },
35782
35783         hide : function(msgEl, f){
35784             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35785         }
35786     },
35787
35788     slideRight : {
35789         show: function(msgEl, f){
35790             msgEl.fixDisplay();
35791             msgEl.alignTo(f.el, 'tl-tr');
35792             msgEl.slideIn('l', {stopFx:true});
35793         },
35794
35795         hide : function(msgEl, f){
35796             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35797         }
35798     }
35799 };/*
35800  * Based on:
35801  * Ext JS Library 1.1.1
35802  * Copyright(c) 2006-2007, Ext JS, LLC.
35803  *
35804  * Originally Released Under LGPL - original licence link has changed is not relivant.
35805  *
35806  * Fork - LGPL
35807  * <script type="text/javascript">
35808  */
35809  
35810
35811 /**
35812  * @class Roo.form.TextField
35813  * @extends Roo.form.Field
35814  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35815  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35816  * @constructor
35817  * Creates a new TextField
35818  * @param {Object} config Configuration options
35819  */
35820 Roo.form.TextField = function(config){
35821     Roo.form.TextField.superclass.constructor.call(this, config);
35822     this.addEvents({
35823         /**
35824          * @event autosize
35825          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35826          * according to the default logic, but this event provides a hook for the developer to apply additional
35827          * logic at runtime to resize the field if needed.
35828              * @param {Roo.form.Field} this This text field
35829              * @param {Number} width The new field width
35830              */
35831         autosize : true
35832     });
35833 };
35834
35835 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35836     /**
35837      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35838      */
35839     grow : false,
35840     /**
35841      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35842      */
35843     growMin : 30,
35844     /**
35845      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35846      */
35847     growMax : 800,
35848     /**
35849      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35850      */
35851     vtype : null,
35852     /**
35853      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35854      */
35855     maskRe : null,
35856     /**
35857      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35858      */
35859     disableKeyFilter : false,
35860     /**
35861      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35862      */
35863     allowBlank : true,
35864     /**
35865      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35866      */
35867     minLength : 0,
35868     /**
35869      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35870      */
35871     maxLength : Number.MAX_VALUE,
35872     /**
35873      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35874      */
35875     minLengthText : "The minimum length for this field is {0}",
35876     /**
35877      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35878      */
35879     maxLengthText : "The maximum length for this field is {0}",
35880     /**
35881      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35882      */
35883     selectOnFocus : false,
35884     /**
35885      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35886      */
35887     blankText : "This field is required",
35888     /**
35889      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35890      * If available, this function will be called only after the basic validators all return true, and will be passed the
35891      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35892      */
35893     validator : null,
35894     /**
35895      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35896      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35897      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35898      */
35899     regex : null,
35900     /**
35901      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35902      */
35903     regexText : "",
35904     /**
35905      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35906      */
35907     emptyText : null,
35908     /**
35909      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35910      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35911      */
35912     emptyClass : 'x-form-empty-field',
35913
35914     // private
35915     initEvents : function(){
35916         Roo.form.TextField.superclass.initEvents.call(this);
35917         if(this.validationEvent == 'keyup'){
35918             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35919             this.el.on('keyup', this.filterValidation, this);
35920         }
35921         else if(this.validationEvent !== false){
35922             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35923         }
35924         if(this.selectOnFocus || this.emptyText){
35925             this.on("focus", this.preFocus, this);
35926             if(this.emptyText){
35927                 this.on('blur', this.postBlur, this);
35928                 this.applyEmptyText();
35929             }
35930         }
35931         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35932             this.el.on("keypress", this.filterKeys, this);
35933         }
35934         if(this.grow){
35935             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35936             this.el.on("click", this.autoSize,  this);
35937         }
35938     },
35939
35940     processValue : function(value){
35941         if(this.stripCharsRe){
35942             var newValue = value.replace(this.stripCharsRe, '');
35943             if(newValue !== value){
35944                 this.setRawValue(newValue);
35945                 return newValue;
35946             }
35947         }
35948         return value;
35949     },
35950
35951     filterValidation : function(e){
35952         if(!e.isNavKeyPress()){
35953             this.validationTask.delay(this.validationDelay);
35954         }
35955     },
35956
35957     // private
35958     onKeyUp : function(e){
35959         if(!e.isNavKeyPress()){
35960             this.autoSize();
35961         }
35962     },
35963
35964     /**
35965      * Resets the current field value to the originally-loaded value and clears any validation messages.
35966      * Also adds emptyText and emptyClass if the original value was blank.
35967      */
35968     reset : function(){
35969         Roo.form.TextField.superclass.reset.call(this);
35970         this.applyEmptyText();
35971     },
35972
35973     applyEmptyText : function(){
35974         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35975             this.setRawValue(this.emptyText);
35976             this.el.addClass(this.emptyClass);
35977         }
35978     },
35979
35980     // private
35981     preFocus : function(){
35982         if(this.emptyText){
35983             if(this.el.dom.value == this.emptyText){
35984                 this.setRawValue('');
35985             }
35986             this.el.removeClass(this.emptyClass);
35987         }
35988         if(this.selectOnFocus){
35989             this.el.dom.select();
35990         }
35991     },
35992
35993     // private
35994     postBlur : function(){
35995         this.applyEmptyText();
35996     },
35997
35998     // private
35999     filterKeys : function(e){
36000         var k = e.getKey();
36001         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36002             return;
36003         }
36004         var c = e.getCharCode(), cc = String.fromCharCode(c);
36005         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36006             return;
36007         }
36008         if(!this.maskRe.test(cc)){
36009             e.stopEvent();
36010         }
36011     },
36012
36013     setValue : function(v){
36014         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36015             this.el.removeClass(this.emptyClass);
36016         }
36017         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36018         this.applyEmptyText();
36019         this.autoSize();
36020     },
36021
36022     /**
36023      * Validates a value according to the field's validation rules and marks the field as invalid
36024      * if the validation fails
36025      * @param {Mixed} value The value to validate
36026      * @return {Boolean} True if the value is valid, else false
36027      */
36028     validateValue : function(value){
36029         if(value.length < 1 || value === this.emptyText){ // if it's blank
36030              if(this.allowBlank){
36031                 this.clearInvalid();
36032                 return true;
36033              }else{
36034                 this.markInvalid(this.blankText);
36035                 return false;
36036              }
36037         }
36038         if(value.length < this.minLength){
36039             this.markInvalid(String.format(this.minLengthText, this.minLength));
36040             return false;
36041         }
36042         if(value.length > this.maxLength){
36043             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36044             return false;
36045         }
36046         if(this.vtype){
36047             var vt = Roo.form.VTypes;
36048             if(!vt[this.vtype](value, this)){
36049                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36050                 return false;
36051             }
36052         }
36053         if(typeof this.validator == "function"){
36054             var msg = this.validator(value);
36055             if(msg !== true){
36056                 this.markInvalid(msg);
36057                 return false;
36058             }
36059         }
36060         if(this.regex && !this.regex.test(value)){
36061             this.markInvalid(this.regexText);
36062             return false;
36063         }
36064         return true;
36065     },
36066
36067     /**
36068      * Selects text in this field
36069      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36070      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36071      */
36072     selectText : function(start, end){
36073         var v = this.getRawValue();
36074         if(v.length > 0){
36075             start = start === undefined ? 0 : start;
36076             end = end === undefined ? v.length : end;
36077             var d = this.el.dom;
36078             if(d.setSelectionRange){
36079                 d.setSelectionRange(start, end);
36080             }else if(d.createTextRange){
36081                 var range = d.createTextRange();
36082                 range.moveStart("character", start);
36083                 range.moveEnd("character", v.length-end);
36084                 range.select();
36085             }
36086         }
36087     },
36088
36089     /**
36090      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36091      * This only takes effect if grow = true, and fires the autosize event.
36092      */
36093     autoSize : function(){
36094         if(!this.grow || !this.rendered){
36095             return;
36096         }
36097         if(!this.metrics){
36098             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36099         }
36100         var el = this.el;
36101         var v = el.dom.value;
36102         var d = document.createElement('div');
36103         d.appendChild(document.createTextNode(v));
36104         v = d.innerHTML;
36105         d = null;
36106         v += "&#160;";
36107         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36108         this.el.setWidth(w);
36109         this.fireEvent("autosize", this, w);
36110     }
36111 });/*
36112  * Based on:
36113  * Ext JS Library 1.1.1
36114  * Copyright(c) 2006-2007, Ext JS, LLC.
36115  *
36116  * Originally Released Under LGPL - original licence link has changed is not relivant.
36117  *
36118  * Fork - LGPL
36119  * <script type="text/javascript">
36120  */
36121  
36122 /**
36123  * @class Roo.form.Hidden
36124  * @extends Roo.form.TextField
36125  * Simple Hidden element used on forms 
36126  * 
36127  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36128  * 
36129  * @constructor
36130  * Creates a new Hidden form element.
36131  * @param {Object} config Configuration options
36132  */
36133
36134
36135
36136 // easy hidden field...
36137 Roo.form.Hidden = function(config){
36138     Roo.form.Hidden.superclass.constructor.call(this, config);
36139 };
36140   
36141 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36142     fieldLabel:      '',
36143     inputType:      'hidden',
36144     width:          50,
36145     allowBlank:     true,
36146     labelSeparator: '',
36147     hidden:         true,
36148     itemCls :       'x-form-item-display-none'
36149
36150
36151 });
36152
36153
36154 /*
36155  * Based on:
36156  * Ext JS Library 1.1.1
36157  * Copyright(c) 2006-2007, Ext JS, LLC.
36158  *
36159  * Originally Released Under LGPL - original licence link has changed is not relivant.
36160  *
36161  * Fork - LGPL
36162  * <script type="text/javascript">
36163  */
36164  
36165 /**
36166  * @class Roo.form.TriggerField
36167  * @extends Roo.form.TextField
36168  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36169  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36170  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36171  * for which you can provide a custom implementation.  For example:
36172  * <pre><code>
36173 var trigger = new Roo.form.TriggerField();
36174 trigger.onTriggerClick = myTriggerFn;
36175 trigger.applyTo('my-field');
36176 </code></pre>
36177  *
36178  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36179  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36180  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36181  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36182  * @constructor
36183  * Create a new TriggerField.
36184  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36185  * to the base TextField)
36186  */
36187 Roo.form.TriggerField = function(config){
36188     this.mimicing = false;
36189     Roo.form.TriggerField.superclass.constructor.call(this, config);
36190 };
36191
36192 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36193     /**
36194      * @cfg {String} triggerClass A CSS class to apply to the trigger
36195      */
36196     /**
36197      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36198      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36199      */
36200     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36201     /**
36202      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36203      */
36204     hideTrigger:false,
36205
36206     /** @cfg {Boolean} grow @hide */
36207     /** @cfg {Number} growMin @hide */
36208     /** @cfg {Number} growMax @hide */
36209
36210     /**
36211      * @hide 
36212      * @method
36213      */
36214     autoSize: Roo.emptyFn,
36215     // private
36216     monitorTab : true,
36217     // private
36218     deferHeight : true,
36219
36220     
36221     actionMode : 'wrap',
36222     // private
36223     onResize : function(w, h){
36224         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36225         if(typeof w == 'number'){
36226             var x = w - this.trigger.getWidth();
36227             this.el.setWidth(this.adjustWidth('input', x));
36228             this.trigger.setStyle('left', x+'px');
36229         }
36230     },
36231
36232     // private
36233     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36234
36235     // private
36236     getResizeEl : function(){
36237         return this.wrap;
36238     },
36239
36240     // private
36241     getPositionEl : function(){
36242         return this.wrap;
36243     },
36244
36245     // private
36246     alignErrorIcon : function(){
36247         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36248     },
36249
36250     // private
36251     onRender : function(ct, position){
36252         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36253         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36254         this.trigger = this.wrap.createChild(this.triggerConfig ||
36255                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36256         if(this.hideTrigger){
36257             this.trigger.setDisplayed(false);
36258         }
36259         this.initTrigger();
36260         if(!this.width){
36261             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36262         }
36263     },
36264
36265     // private
36266     initTrigger : function(){
36267         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36268         this.trigger.addClassOnOver('x-form-trigger-over');
36269         this.trigger.addClassOnClick('x-form-trigger-click');
36270     },
36271
36272     // private
36273     onDestroy : function(){
36274         if(this.trigger){
36275             this.trigger.removeAllListeners();
36276             this.trigger.remove();
36277         }
36278         if(this.wrap){
36279             this.wrap.remove();
36280         }
36281         Roo.form.TriggerField.superclass.onDestroy.call(this);
36282     },
36283
36284     // private
36285     onFocus : function(){
36286         Roo.form.TriggerField.superclass.onFocus.call(this);
36287         if(!this.mimicing){
36288             this.wrap.addClass('x-trigger-wrap-focus');
36289             this.mimicing = true;
36290             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36291             if(this.monitorTab){
36292                 this.el.on("keydown", this.checkTab, this);
36293             }
36294         }
36295     },
36296
36297     // private
36298     checkTab : function(e){
36299         if(e.getKey() == e.TAB){
36300             this.triggerBlur();
36301         }
36302     },
36303
36304     // private
36305     onBlur : function(){
36306         // do nothing
36307     },
36308
36309     // private
36310     mimicBlur : function(e, t){
36311         if(!this.wrap.contains(t) && this.validateBlur()){
36312             this.triggerBlur();
36313         }
36314     },
36315
36316     // private
36317     triggerBlur : function(){
36318         this.mimicing = false;
36319         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36320         if(this.monitorTab){
36321             this.el.un("keydown", this.checkTab, this);
36322         }
36323         this.wrap.removeClass('x-trigger-wrap-focus');
36324         Roo.form.TriggerField.superclass.onBlur.call(this);
36325     },
36326
36327     // private
36328     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36329     validateBlur : function(e, t){
36330         return true;
36331     },
36332
36333     // private
36334     onDisable : function(){
36335         Roo.form.TriggerField.superclass.onDisable.call(this);
36336         if(this.wrap){
36337             this.wrap.addClass('x-item-disabled');
36338         }
36339     },
36340
36341     // private
36342     onEnable : function(){
36343         Roo.form.TriggerField.superclass.onEnable.call(this);
36344         if(this.wrap){
36345             this.wrap.removeClass('x-item-disabled');
36346         }
36347     },
36348
36349     // private
36350     onShow : function(){
36351         var ae = this.getActionEl();
36352         
36353         if(ae){
36354             ae.dom.style.display = '';
36355             ae.dom.style.visibility = 'visible';
36356         }
36357     },
36358
36359     // private
36360     
36361     onHide : function(){
36362         var ae = this.getActionEl();
36363         ae.dom.style.display = 'none';
36364     },
36365
36366     /**
36367      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36368      * by an implementing function.
36369      * @method
36370      * @param {EventObject} e
36371      */
36372     onTriggerClick : Roo.emptyFn
36373 });
36374
36375 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36376 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36377 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36378 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36379     initComponent : function(){
36380         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36381
36382         this.triggerConfig = {
36383             tag:'span', cls:'x-form-twin-triggers', cn:[
36384             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36385             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36386         ]};
36387     },
36388
36389     getTrigger : function(index){
36390         return this.triggers[index];
36391     },
36392
36393     initTrigger : function(){
36394         var ts = this.trigger.select('.x-form-trigger', true);
36395         this.wrap.setStyle('overflow', 'hidden');
36396         var triggerField = this;
36397         ts.each(function(t, all, index){
36398             t.hide = function(){
36399                 var w = triggerField.wrap.getWidth();
36400                 this.dom.style.display = 'none';
36401                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36402             };
36403             t.show = function(){
36404                 var w = triggerField.wrap.getWidth();
36405                 this.dom.style.display = '';
36406                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36407             };
36408             var triggerIndex = 'Trigger'+(index+1);
36409
36410             if(this['hide'+triggerIndex]){
36411                 t.dom.style.display = 'none';
36412             }
36413             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36414             t.addClassOnOver('x-form-trigger-over');
36415             t.addClassOnClick('x-form-trigger-click');
36416         }, this);
36417         this.triggers = ts.elements;
36418     },
36419
36420     onTrigger1Click : Roo.emptyFn,
36421     onTrigger2Click : Roo.emptyFn
36422 });/*
36423  * Based on:
36424  * Ext JS Library 1.1.1
36425  * Copyright(c) 2006-2007, Ext JS, LLC.
36426  *
36427  * Originally Released Under LGPL - original licence link has changed is not relivant.
36428  *
36429  * Fork - LGPL
36430  * <script type="text/javascript">
36431  */
36432  
36433 /**
36434  * @class Roo.form.TextArea
36435  * @extends Roo.form.TextField
36436  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36437  * support for auto-sizing.
36438  * @constructor
36439  * Creates a new TextArea
36440  * @param {Object} config Configuration options
36441  */
36442 Roo.form.TextArea = function(config){
36443     Roo.form.TextArea.superclass.constructor.call(this, config);
36444     // these are provided exchanges for backwards compat
36445     // minHeight/maxHeight were replaced by growMin/growMax to be
36446     // compatible with TextField growing config values
36447     if(this.minHeight !== undefined){
36448         this.growMin = this.minHeight;
36449     }
36450     if(this.maxHeight !== undefined){
36451         this.growMax = this.maxHeight;
36452     }
36453 };
36454
36455 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36456     /**
36457      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36458      */
36459     growMin : 60,
36460     /**
36461      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36462      */
36463     growMax: 1000,
36464     /**
36465      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36466      * in the field (equivalent to setting overflow: hidden, defaults to false)
36467      */
36468     preventScrollbars: false,
36469     /**
36470      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36471      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36472      */
36473
36474     // private
36475     onRender : function(ct, position){
36476         if(!this.el){
36477             this.defaultAutoCreate = {
36478                 tag: "textarea",
36479                 style:"width:300px;height:60px;",
36480                 autocomplete: "off"
36481             };
36482         }
36483         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36484         if(this.grow){
36485             this.textSizeEl = Roo.DomHelper.append(document.body, {
36486                 tag: "pre", cls: "x-form-grow-sizer"
36487             });
36488             if(this.preventScrollbars){
36489                 this.el.setStyle("overflow", "hidden");
36490             }
36491             this.el.setHeight(this.growMin);
36492         }
36493     },
36494
36495     onDestroy : function(){
36496         if(this.textSizeEl){
36497             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36498         }
36499         Roo.form.TextArea.superclass.onDestroy.call(this);
36500     },
36501
36502     // private
36503     onKeyUp : function(e){
36504         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36505             this.autoSize();
36506         }
36507     },
36508
36509     /**
36510      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36511      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36512      */
36513     autoSize : function(){
36514         if(!this.grow || !this.textSizeEl){
36515             return;
36516         }
36517         var el = this.el;
36518         var v = el.dom.value;
36519         var ts = this.textSizeEl;
36520
36521         ts.innerHTML = '';
36522         ts.appendChild(document.createTextNode(v));
36523         v = ts.innerHTML;
36524
36525         Roo.fly(ts).setWidth(this.el.getWidth());
36526         if(v.length < 1){
36527             v = "&#160;&#160;";
36528         }else{
36529             if(Roo.isIE){
36530                 v = v.replace(/\n/g, '<p>&#160;</p>');
36531             }
36532             v += "&#160;\n&#160;";
36533         }
36534         ts.innerHTML = v;
36535         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36536         if(h != this.lastHeight){
36537             this.lastHeight = h;
36538             this.el.setHeight(h);
36539             this.fireEvent("autosize", this, h);
36540         }
36541     }
36542 });/*
36543  * Based on:
36544  * Ext JS Library 1.1.1
36545  * Copyright(c) 2006-2007, Ext JS, LLC.
36546  *
36547  * Originally Released Under LGPL - original licence link has changed is not relivant.
36548  *
36549  * Fork - LGPL
36550  * <script type="text/javascript">
36551  */
36552  
36553
36554 /**
36555  * @class Roo.form.NumberField
36556  * @extends Roo.form.TextField
36557  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36558  * @constructor
36559  * Creates a new NumberField
36560  * @param {Object} config Configuration options
36561  */
36562 Roo.form.NumberField = function(config){
36563     Roo.form.NumberField.superclass.constructor.call(this, config);
36564 };
36565
36566 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36567     /**
36568      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36569      */
36570     fieldClass: "x-form-field x-form-num-field",
36571     /**
36572      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36573      */
36574     allowDecimals : true,
36575     /**
36576      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36577      */
36578     decimalSeparator : ".",
36579     /**
36580      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36581      */
36582     decimalPrecision : 2,
36583     /**
36584      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36585      */
36586     allowNegative : true,
36587     /**
36588      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36589      */
36590     minValue : Number.NEGATIVE_INFINITY,
36591     /**
36592      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36593      */
36594     maxValue : Number.MAX_VALUE,
36595     /**
36596      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36597      */
36598     minText : "The minimum value for this field is {0}",
36599     /**
36600      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36601      */
36602     maxText : "The maximum value for this field is {0}",
36603     /**
36604      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36605      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36606      */
36607     nanText : "{0} is not a valid number",
36608
36609     // private
36610     initEvents : function(){
36611         Roo.form.NumberField.superclass.initEvents.call(this);
36612         var allowed = "0123456789";
36613         if(this.allowDecimals){
36614             allowed += this.decimalSeparator;
36615         }
36616         if(this.allowNegative){
36617             allowed += "-";
36618         }
36619         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36620         var keyPress = function(e){
36621             var k = e.getKey();
36622             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36623                 return;
36624             }
36625             var c = e.getCharCode();
36626             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36627                 e.stopEvent();
36628             }
36629         };
36630         this.el.on("keypress", keyPress, this);
36631     },
36632
36633     // private
36634     validateValue : function(value){
36635         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36636             return false;
36637         }
36638         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36639              return true;
36640         }
36641         var num = this.parseValue(value);
36642         if(isNaN(num)){
36643             this.markInvalid(String.format(this.nanText, value));
36644             return false;
36645         }
36646         if(num < this.minValue){
36647             this.markInvalid(String.format(this.minText, this.minValue));
36648             return false;
36649         }
36650         if(num > this.maxValue){
36651             this.markInvalid(String.format(this.maxText, this.maxValue));
36652             return false;
36653         }
36654         return true;
36655     },
36656
36657     getValue : function(){
36658         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36659     },
36660
36661     // private
36662     parseValue : function(value){
36663         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36664         return isNaN(value) ? '' : value;
36665     },
36666
36667     // private
36668     fixPrecision : function(value){
36669         var nan = isNaN(value);
36670         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36671             return nan ? '' : value;
36672         }
36673         return parseFloat(value).toFixed(this.decimalPrecision);
36674     },
36675
36676     setValue : function(v){
36677         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36678     },
36679
36680     // private
36681     decimalPrecisionFcn : function(v){
36682         return Math.floor(v);
36683     },
36684
36685     beforeBlur : function(){
36686         var v = this.parseValue(this.getRawValue());
36687         if(v){
36688             this.setValue(this.fixPrecision(v));
36689         }
36690     }
36691 });/*
36692  * Based on:
36693  * Ext JS Library 1.1.1
36694  * Copyright(c) 2006-2007, Ext JS, LLC.
36695  *
36696  * Originally Released Under LGPL - original licence link has changed is not relivant.
36697  *
36698  * Fork - LGPL
36699  * <script type="text/javascript">
36700  */
36701  
36702 /**
36703  * @class Roo.form.DateField
36704  * @extends Roo.form.TriggerField
36705  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36706 * @constructor
36707 * Create a new DateField
36708 * @param {Object} config
36709  */
36710 Roo.form.DateField = function(config){
36711     Roo.form.DateField.superclass.constructor.call(this, config);
36712     
36713       this.addEvents({
36714          
36715         /**
36716          * @event select
36717          * Fires when a date is selected
36718              * @param {Roo.form.DateField} combo This combo box
36719              * @param {Date} date The date selected
36720              */
36721         'select' : true
36722          
36723     });
36724     
36725     
36726     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36727     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36728     this.ddMatch = null;
36729     if(this.disabledDates){
36730         var dd = this.disabledDates;
36731         var re = "(?:";
36732         for(var i = 0; i < dd.length; i++){
36733             re += dd[i];
36734             if(i != dd.length-1) re += "|";
36735         }
36736         this.ddMatch = new RegExp(re + ")");
36737     }
36738 };
36739
36740 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36741     /**
36742      * @cfg {String} format
36743      * The default date format string which can be overriden for localization support.  The format must be
36744      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36745      */
36746     format : "m/d/y",
36747     /**
36748      * @cfg {String} altFormats
36749      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36750      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36751      */
36752     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36753     /**
36754      * @cfg {Array} disabledDays
36755      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36756      */
36757     disabledDays : null,
36758     /**
36759      * @cfg {String} disabledDaysText
36760      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36761      */
36762     disabledDaysText : "Disabled",
36763     /**
36764      * @cfg {Array} disabledDates
36765      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36766      * expression so they are very powerful. Some examples:
36767      * <ul>
36768      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36769      * <li>["03/08", "09/16"] would disable those days for every year</li>
36770      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36771      * <li>["03/../2006"] would disable every day in March 2006</li>
36772      * <li>["^03"] would disable every day in every March</li>
36773      * </ul>
36774      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36775      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36776      */
36777     disabledDates : null,
36778     /**
36779      * @cfg {String} disabledDatesText
36780      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36781      */
36782     disabledDatesText : "Disabled",
36783     /**
36784      * @cfg {Date/String} minValue
36785      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36786      * valid format (defaults to null).
36787      */
36788     minValue : null,
36789     /**
36790      * @cfg {Date/String} maxValue
36791      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36792      * valid format (defaults to null).
36793      */
36794     maxValue : null,
36795     /**
36796      * @cfg {String} minText
36797      * The error text to display when the date in the cell is before minValue (defaults to
36798      * 'The date in this field must be after {minValue}').
36799      */
36800     minText : "The date in this field must be equal to or after {0}",
36801     /**
36802      * @cfg {String} maxText
36803      * The error text to display when the date in the cell is after maxValue (defaults to
36804      * 'The date in this field must be before {maxValue}').
36805      */
36806     maxText : "The date in this field must be equal to or before {0}",
36807     /**
36808      * @cfg {String} invalidText
36809      * The error text to display when the date in the field is invalid (defaults to
36810      * '{value} is not a valid date - it must be in the format {format}').
36811      */
36812     invalidText : "{0} is not a valid date - it must be in the format {1}",
36813     /**
36814      * @cfg {String} triggerClass
36815      * An additional CSS class used to style the trigger button.  The trigger will always get the
36816      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36817      * which displays a calendar icon).
36818      */
36819     triggerClass : 'x-form-date-trigger',
36820     
36821
36822     /**
36823      * @cfg {bool} useIso
36824      * if enabled, then the date field will use a hidden field to store the 
36825      * real value as iso formated date. default (false)
36826      */ 
36827     useIso : false,
36828     /**
36829      * @cfg {String/Object} autoCreate
36830      * A DomHelper element spec, or true for a default element spec (defaults to
36831      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36832      */ 
36833     // private
36834     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36835     
36836     // private
36837     hiddenField: false,
36838     
36839     onRender : function(ct, position)
36840     {
36841         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36842         if (this.useIso) {
36843             this.el.dom.removeAttribute('name'); 
36844             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36845                     'before', true);
36846             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36847             // prevent input submission
36848             this.hiddenName = this.name;
36849         }
36850             
36851             
36852     },
36853     
36854     // private
36855     validateValue : function(value)
36856     {
36857         value = this.formatDate(value);
36858         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36859             return false;
36860         }
36861         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36862              return true;
36863         }
36864         var svalue = value;
36865         value = this.parseDate(value);
36866         if(!value){
36867             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36868             return false;
36869         }
36870         var time = value.getTime();
36871         if(this.minValue && time < this.minValue.getTime()){
36872             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36873             return false;
36874         }
36875         if(this.maxValue && time > this.maxValue.getTime()){
36876             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36877             return false;
36878         }
36879         if(this.disabledDays){
36880             var day = value.getDay();
36881             for(var i = 0; i < this.disabledDays.length; i++) {
36882                 if(day === this.disabledDays[i]){
36883                     this.markInvalid(this.disabledDaysText);
36884                     return false;
36885                 }
36886             }
36887         }
36888         var fvalue = this.formatDate(value);
36889         if(this.ddMatch && this.ddMatch.test(fvalue)){
36890             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36891             return false;
36892         }
36893         return true;
36894     },
36895
36896     // private
36897     // Provides logic to override the default TriggerField.validateBlur which just returns true
36898     validateBlur : function(){
36899         return !this.menu || !this.menu.isVisible();
36900     },
36901
36902     /**
36903      * Returns the current date value of the date field.
36904      * @return {Date} The date value
36905      */
36906     getValue : function(){
36907         
36908         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36909     },
36910
36911     /**
36912      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36913      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36914      * (the default format used is "m/d/y").
36915      * <br />Usage:
36916      * <pre><code>
36917 //All of these calls set the same date value (May 4, 2006)
36918
36919 //Pass a date object:
36920 var dt = new Date('5/4/06');
36921 dateField.setValue(dt);
36922
36923 //Pass a date string (default format):
36924 dateField.setValue('5/4/06');
36925
36926 //Pass a date string (custom format):
36927 dateField.format = 'Y-m-d';
36928 dateField.setValue('2006-5-4');
36929 </code></pre>
36930      * @param {String/Date} date The date or valid date string
36931      */
36932     setValue : function(date){
36933         if (this.hiddenField) {
36934             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36935         }
36936         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36937     },
36938
36939     // private
36940     parseDate : function(value){
36941         if(!value || value instanceof Date){
36942             return value;
36943         }
36944         var v = Date.parseDate(value, this.format);
36945         if(!v && this.altFormats){
36946             if(!this.altFormatsArray){
36947                 this.altFormatsArray = this.altFormats.split("|");
36948             }
36949             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36950                 v = Date.parseDate(value, this.altFormatsArray[i]);
36951             }
36952         }
36953         return v;
36954     },
36955
36956     // private
36957     formatDate : function(date, fmt){
36958         return (!date || !(date instanceof Date)) ?
36959                date : date.dateFormat(fmt || this.format);
36960     },
36961
36962     // private
36963     menuListeners : {
36964         select: function(m, d){
36965             this.setValue(d);
36966             this.fireEvent('select', this, d);
36967         },
36968         show : function(){ // retain focus styling
36969             this.onFocus();
36970         },
36971         hide : function(){
36972             this.focus.defer(10, this);
36973             var ml = this.menuListeners;
36974             this.menu.un("select", ml.select,  this);
36975             this.menu.un("show", ml.show,  this);
36976             this.menu.un("hide", ml.hide,  this);
36977         }
36978     },
36979
36980     // private
36981     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36982     onTriggerClick : function(){
36983         if(this.disabled){
36984             return;
36985         }
36986         if(this.menu == null){
36987             this.menu = new Roo.menu.DateMenu();
36988         }
36989         Roo.apply(this.menu.picker,  {
36990             showClear: this.allowBlank,
36991             minDate : this.minValue,
36992             maxDate : this.maxValue,
36993             disabledDatesRE : this.ddMatch,
36994             disabledDatesText : this.disabledDatesText,
36995             disabledDays : this.disabledDays,
36996             disabledDaysText : this.disabledDaysText,
36997             format : this.format,
36998             minText : String.format(this.minText, this.formatDate(this.minValue)),
36999             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37000         });
37001         this.menu.on(Roo.apply({}, this.menuListeners, {
37002             scope:this
37003         }));
37004         this.menu.picker.setValue(this.getValue() || new Date());
37005         this.menu.show(this.el, "tl-bl?");
37006     },
37007
37008     beforeBlur : function(){
37009         var v = this.parseDate(this.getRawValue());
37010         if(v){
37011             this.setValue(v);
37012         }
37013     }
37014
37015     /** @cfg {Boolean} grow @hide */
37016     /** @cfg {Number} growMin @hide */
37017     /** @cfg {Number} growMax @hide */
37018     /**
37019      * @hide
37020      * @method autoSize
37021      */
37022 });/*
37023  * Based on:
37024  * Ext JS Library 1.1.1
37025  * Copyright(c) 2006-2007, Ext JS, LLC.
37026  *
37027  * Originally Released Under LGPL - original licence link has changed is not relivant.
37028  *
37029  * Fork - LGPL
37030  * <script type="text/javascript">
37031  */
37032  
37033
37034 /**
37035  * @class Roo.form.ComboBox
37036  * @extends Roo.form.TriggerField
37037  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37038  * @constructor
37039  * Create a new ComboBox.
37040  * @param {Object} config Configuration options
37041  */
37042 Roo.form.ComboBox = function(config){
37043     Roo.form.ComboBox.superclass.constructor.call(this, config);
37044     this.addEvents({
37045         /**
37046          * @event expand
37047          * Fires when the dropdown list is expanded
37048              * @param {Roo.form.ComboBox} combo This combo box
37049              */
37050         'expand' : true,
37051         /**
37052          * @event collapse
37053          * Fires when the dropdown list is collapsed
37054              * @param {Roo.form.ComboBox} combo This combo box
37055              */
37056         'collapse' : true,
37057         /**
37058          * @event beforeselect
37059          * Fires before a list item is selected. Return false to cancel the selection.
37060              * @param {Roo.form.ComboBox} combo This combo box
37061              * @param {Roo.data.Record} record The data record returned from the underlying store
37062              * @param {Number} index The index of the selected item in the dropdown list
37063              */
37064         'beforeselect' : true,
37065         /**
37066          * @event select
37067          * Fires when a list item is selected
37068              * @param {Roo.form.ComboBox} combo This combo box
37069              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37070              * @param {Number} index The index of the selected item in the dropdown list
37071              */
37072         'select' : true,
37073         /**
37074          * @event beforequery
37075          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37076          * The event object passed has these properties:
37077              * @param {Roo.form.ComboBox} combo This combo box
37078              * @param {String} query The query
37079              * @param {Boolean} forceAll true to force "all" query
37080              * @param {Boolean} cancel true to cancel the query
37081              * @param {Object} e The query event object
37082              */
37083         'beforequery': true,
37084          /**
37085          * @event add
37086          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37087              * @param {Roo.form.ComboBox} combo This combo box
37088              */
37089         'add' : true,
37090         /**
37091          * @event edit
37092          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37093              * @param {Roo.form.ComboBox} combo This combo box
37094              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37095              */
37096         'edit' : true
37097         
37098         
37099     });
37100     if(this.transform){
37101         this.allowDomMove = false;
37102         var s = Roo.getDom(this.transform);
37103         if(!this.hiddenName){
37104             this.hiddenName = s.name;
37105         }
37106         if(!this.store){
37107             this.mode = 'local';
37108             var d = [], opts = s.options;
37109             for(var i = 0, len = opts.length;i < len; i++){
37110                 var o = opts[i];
37111                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37112                 if(o.selected) {
37113                     this.value = value;
37114                 }
37115                 d.push([value, o.text]);
37116             }
37117             this.store = new Roo.data.SimpleStore({
37118                 'id': 0,
37119                 fields: ['value', 'text'],
37120                 data : d
37121             });
37122             this.valueField = 'value';
37123             this.displayField = 'text';
37124         }
37125         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37126         if(!this.lazyRender){
37127             this.target = true;
37128             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37129             s.parentNode.removeChild(s); // remove it
37130             this.render(this.el.parentNode);
37131         }else{
37132             s.parentNode.removeChild(s); // remove it
37133         }
37134
37135     }
37136     if (this.store) {
37137         this.store = Roo.factory(this.store, Roo.data);
37138     }
37139     
37140     this.selectedIndex = -1;
37141     if(this.mode == 'local'){
37142         if(config.queryDelay === undefined){
37143             this.queryDelay = 10;
37144         }
37145         if(config.minChars === undefined){
37146             this.minChars = 0;
37147         }
37148     }
37149 };
37150
37151 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37152     /**
37153      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37154      */
37155     /**
37156      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37157      * rendering into an Roo.Editor, defaults to false)
37158      */
37159     /**
37160      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37161      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37162      */
37163     /**
37164      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37165      */
37166     /**
37167      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37168      * the dropdown list (defaults to undefined, with no header element)
37169      */
37170
37171      /**
37172      * @cfg {String/Roo.Template} tpl The template to use to render the output
37173      */
37174      
37175     // private
37176     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37177     /**
37178      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37179      */
37180     listWidth: undefined,
37181     /**
37182      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37183      * mode = 'remote' or 'text' if mode = 'local')
37184      */
37185     displayField: undefined,
37186     /**
37187      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37188      * mode = 'remote' or 'value' if mode = 'local'). 
37189      * Note: use of a valueField requires the user make a selection
37190      * in order for a value to be mapped.
37191      */
37192     valueField: undefined,
37193     /**
37194      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37195      * field's data value (defaults to the underlying DOM element's name)
37196      */
37197     hiddenName: undefined,
37198     /**
37199      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37200      */
37201     listClass: '',
37202     /**
37203      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37204      */
37205     selectedClass: 'x-combo-selected',
37206     /**
37207      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37208      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37209      * which displays a downward arrow icon).
37210      */
37211     triggerClass : 'x-form-arrow-trigger',
37212     /**
37213      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37214      */
37215     shadow:'sides',
37216     /**
37217      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37218      * anchor positions (defaults to 'tl-bl')
37219      */
37220     listAlign: 'tl-bl?',
37221     /**
37222      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37223      */
37224     maxHeight: 300,
37225     /**
37226      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37227      * query specified by the allQuery config option (defaults to 'query')
37228      */
37229     triggerAction: 'query',
37230     /**
37231      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37232      * (defaults to 4, does not apply if editable = false)
37233      */
37234     minChars : 4,
37235     /**
37236      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37237      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37238      */
37239     typeAhead: false,
37240     /**
37241      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37242      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37243      */
37244     queryDelay: 500,
37245     /**
37246      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37247      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37248      */
37249     pageSize: 0,
37250     /**
37251      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37252      * when editable = true (defaults to false)
37253      */
37254     selectOnFocus:false,
37255     /**
37256      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37257      */
37258     queryParam: 'query',
37259     /**
37260      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37261      * when mode = 'remote' (defaults to 'Loading...')
37262      */
37263     loadingText: 'Loading...',
37264     /**
37265      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37266      */
37267     resizable: false,
37268     /**
37269      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37270      */
37271     handleHeight : 8,
37272     /**
37273      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37274      * traditional select (defaults to true)
37275      */
37276     editable: true,
37277     /**
37278      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37279      */
37280     allQuery: '',
37281     /**
37282      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37283      */
37284     mode: 'remote',
37285     /**
37286      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37287      * listWidth has a higher value)
37288      */
37289     minListWidth : 70,
37290     /**
37291      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37292      * allow the user to set arbitrary text into the field (defaults to false)
37293      */
37294     forceSelection:false,
37295     /**
37296      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37297      * if typeAhead = true (defaults to 250)
37298      */
37299     typeAheadDelay : 250,
37300     /**
37301      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37302      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37303      */
37304     valueNotFoundText : undefined,
37305     /**
37306      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37307      */
37308     blockFocus : false,
37309     
37310     /**
37311      * @cfg {Boolean} disableClear Disable showing of clear button.
37312      */
37313     disableClear : false,
37314     /**
37315      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37316      */
37317     alwaysQuery : false,
37318     
37319     //private
37320     addicon : false,
37321     editicon: false,
37322     
37323     
37324     // private
37325     onRender : function(ct, position){
37326         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37327         if(this.hiddenName){
37328             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37329                     'before', true);
37330             this.hiddenField.value =
37331                 this.hiddenValue !== undefined ? this.hiddenValue :
37332                 this.value !== undefined ? this.value : '';
37333
37334             // prevent input submission
37335             this.el.dom.removeAttribute('name');
37336         }
37337         if(Roo.isGecko){
37338             this.el.dom.setAttribute('autocomplete', 'off');
37339         }
37340
37341         var cls = 'x-combo-list';
37342
37343         this.list = new Roo.Layer({
37344             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37345         });
37346
37347         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37348         this.list.setWidth(lw);
37349         this.list.swallowEvent('mousewheel');
37350         this.assetHeight = 0;
37351
37352         if(this.title){
37353             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37354             this.assetHeight += this.header.getHeight();
37355         }
37356
37357         this.innerList = this.list.createChild({cls:cls+'-inner'});
37358         this.innerList.on('mouseover', this.onViewOver, this);
37359         this.innerList.on('mousemove', this.onViewMove, this);
37360         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37361         
37362         if(this.allowBlank && !this.pageSize && !this.disableClear){
37363             this.footer = this.list.createChild({cls:cls+'-ft'});
37364             this.pageTb = new Roo.Toolbar(this.footer);
37365            
37366         }
37367         if(this.pageSize){
37368             this.footer = this.list.createChild({cls:cls+'-ft'});
37369             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37370                     {pageSize: this.pageSize});
37371             
37372         }
37373         
37374         if (this.pageTb && this.allowBlank && !this.disableClear) {
37375             var _this = this;
37376             this.pageTb.add(new Roo.Toolbar.Fill(), {
37377                 cls: 'x-btn-icon x-btn-clear',
37378                 text: '&#160;',
37379                 handler: function()
37380                 {
37381                     _this.collapse();
37382                     _this.clearValue();
37383                     _this.onSelect(false, -1);
37384                 }
37385             });
37386         }
37387         if (this.footer) {
37388             this.assetHeight += this.footer.getHeight();
37389         }
37390         
37391
37392         if(!this.tpl){
37393             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37394         }
37395
37396         this.view = new Roo.View(this.innerList, this.tpl, {
37397             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37398         });
37399
37400         this.view.on('click', this.onViewClick, this);
37401
37402         this.store.on('beforeload', this.onBeforeLoad, this);
37403         this.store.on('load', this.onLoad, this);
37404         this.store.on('loadexception', this.collapse, this);
37405
37406         if(this.resizable){
37407             this.resizer = new Roo.Resizable(this.list,  {
37408                pinned:true, handles:'se'
37409             });
37410             this.resizer.on('resize', function(r, w, h){
37411                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37412                 this.listWidth = w;
37413                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37414                 this.restrictHeight();
37415             }, this);
37416             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37417         }
37418         if(!this.editable){
37419             this.editable = true;
37420             this.setEditable(false);
37421         }  
37422         
37423         
37424         if (typeof(this.events.add.listeners) != 'undefined') {
37425             
37426             this.addicon = this.wrap.createChild(
37427                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37428        
37429             this.addicon.on('click', function(e) {
37430                 this.fireEvent('add', this);
37431             }, this);
37432         }
37433         if (typeof(this.events.edit.listeners) != 'undefined') {
37434             
37435             this.editicon = this.wrap.createChild(
37436                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37437             if (this.addicon) {
37438                 this.editicon.setStyle('margin-left', '40px');
37439             }
37440             this.editicon.on('click', function(e) {
37441                 
37442                 // we fire even  if inothing is selected..
37443                 this.fireEvent('edit', this, this.lastData );
37444                 
37445             }, this);
37446         }
37447         
37448         
37449         
37450     },
37451
37452     // private
37453     initEvents : function(){
37454         Roo.form.ComboBox.superclass.initEvents.call(this);
37455
37456         this.keyNav = new Roo.KeyNav(this.el, {
37457             "up" : function(e){
37458                 this.inKeyMode = true;
37459                 this.selectPrev();
37460             },
37461
37462             "down" : function(e){
37463                 if(!this.isExpanded()){
37464                     this.onTriggerClick();
37465                 }else{
37466                     this.inKeyMode = true;
37467                     this.selectNext();
37468                 }
37469             },
37470
37471             "enter" : function(e){
37472                 this.onViewClick();
37473                 //return true;
37474             },
37475
37476             "esc" : function(e){
37477                 this.collapse();
37478             },
37479
37480             "tab" : function(e){
37481                 this.onViewClick(false);
37482                 return true;
37483             },
37484
37485             scope : this,
37486
37487             doRelay : function(foo, bar, hname){
37488                 if(hname == 'down' || this.scope.isExpanded()){
37489                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37490                 }
37491                 return true;
37492             },
37493
37494             forceKeyDown: true
37495         });
37496         this.queryDelay = Math.max(this.queryDelay || 10,
37497                 this.mode == 'local' ? 10 : 250);
37498         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37499         if(this.typeAhead){
37500             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37501         }
37502         if(this.editable !== false){
37503             this.el.on("keyup", this.onKeyUp, this);
37504         }
37505         if(this.forceSelection){
37506             this.on('blur', this.doForce, this);
37507         }
37508     },
37509
37510     onDestroy : function(){
37511         if(this.view){
37512             this.view.setStore(null);
37513             this.view.el.removeAllListeners();
37514             this.view.el.remove();
37515             this.view.purgeListeners();
37516         }
37517         if(this.list){
37518             this.list.destroy();
37519         }
37520         if(this.store){
37521             this.store.un('beforeload', this.onBeforeLoad, this);
37522             this.store.un('load', this.onLoad, this);
37523             this.store.un('loadexception', this.collapse, this);
37524         }
37525         Roo.form.ComboBox.superclass.onDestroy.call(this);
37526     },
37527
37528     // private
37529     fireKey : function(e){
37530         if(e.isNavKeyPress() && !this.list.isVisible()){
37531             this.fireEvent("specialkey", this, e);
37532         }
37533     },
37534
37535     // private
37536     onResize: function(w, h){
37537         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37538         
37539         if(typeof w != 'number'){
37540             // we do not handle it!?!?
37541             return;
37542         }
37543         var tw = this.trigger.getWidth();
37544         tw += this.addicon ? this.addicon.getWidth() : 0;
37545         tw += this.editicon ? this.editicon.getWidth() : 0;
37546         var x = w - tw;
37547         this.el.setWidth( this.adjustWidth('input', x));
37548             
37549         this.trigger.setStyle('left', x+'px');
37550         
37551         if(this.list && this.listWidth === undefined){
37552             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37553             this.list.setWidth(lw);
37554             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37555         }
37556         
37557     
37558         
37559     },
37560
37561     /**
37562      * Allow or prevent the user from directly editing the field text.  If false is passed,
37563      * the user will only be able to select from the items defined in the dropdown list.  This method
37564      * is the runtime equivalent of setting the 'editable' config option at config time.
37565      * @param {Boolean} value True to allow the user to directly edit the field text
37566      */
37567     setEditable : function(value){
37568         if(value == this.editable){
37569             return;
37570         }
37571         this.editable = value;
37572         if(!value){
37573             this.el.dom.setAttribute('readOnly', true);
37574             this.el.on('mousedown', this.onTriggerClick,  this);
37575             this.el.addClass('x-combo-noedit');
37576         }else{
37577             this.el.dom.setAttribute('readOnly', false);
37578             this.el.un('mousedown', this.onTriggerClick,  this);
37579             this.el.removeClass('x-combo-noedit');
37580         }
37581     },
37582
37583     // private
37584     onBeforeLoad : function(){
37585         if(!this.hasFocus){
37586             return;
37587         }
37588         this.innerList.update(this.loadingText ?
37589                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37590         this.restrictHeight();
37591         this.selectedIndex = -1;
37592     },
37593
37594     // private
37595     onLoad : function(){
37596         if(!this.hasFocus){
37597             return;
37598         }
37599         if(this.store.getCount() > 0){
37600             this.expand();
37601             this.restrictHeight();
37602             if(this.lastQuery == this.allQuery){
37603                 if(this.editable){
37604                     this.el.dom.select();
37605                 }
37606                 if(!this.selectByValue(this.value, true)){
37607                     this.select(0, true);
37608                 }
37609             }else{
37610                 this.selectNext();
37611                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37612                     this.taTask.delay(this.typeAheadDelay);
37613                 }
37614             }
37615         }else{
37616             this.onEmptyResults();
37617         }
37618         //this.el.focus();
37619     },
37620
37621     // private
37622     onTypeAhead : function(){
37623         if(this.store.getCount() > 0){
37624             var r = this.store.getAt(0);
37625             var newValue = r.data[this.displayField];
37626             var len = newValue.length;
37627             var selStart = this.getRawValue().length;
37628             if(selStart != len){
37629                 this.setRawValue(newValue);
37630                 this.selectText(selStart, newValue.length);
37631             }
37632         }
37633     },
37634
37635     // private
37636     onSelect : function(record, index){
37637         if(this.fireEvent('beforeselect', this, record, index) !== false){
37638             this.setFromData(index > -1 ? record.data : false);
37639             this.collapse();
37640             this.fireEvent('select', this, record, index);
37641         }
37642     },
37643
37644     /**
37645      * Returns the currently selected field value or empty string if no value is set.
37646      * @return {String} value The selected value
37647      */
37648     getValue : function(){
37649         if(this.valueField){
37650             return typeof this.value != 'undefined' ? this.value : '';
37651         }else{
37652             return Roo.form.ComboBox.superclass.getValue.call(this);
37653         }
37654     },
37655
37656     /**
37657      * Clears any text/value currently set in the field
37658      */
37659     clearValue : function(){
37660         if(this.hiddenField){
37661             this.hiddenField.value = '';
37662         }
37663         this.value = '';
37664         this.setRawValue('');
37665         this.lastSelectionText = '';
37666         this.applyEmptyText();
37667     },
37668
37669     /**
37670      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37671      * will be displayed in the field.  If the value does not match the data value of an existing item,
37672      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37673      * Otherwise the field will be blank (although the value will still be set).
37674      * @param {String} value The value to match
37675      */
37676     setValue : function(v){
37677         var text = v;
37678         if(this.valueField){
37679             var r = this.findRecord(this.valueField, v);
37680             if(r){
37681                 text = r.data[this.displayField];
37682             }else if(this.valueNotFoundText !== undefined){
37683                 text = this.valueNotFoundText;
37684             }
37685         }
37686         this.lastSelectionText = text;
37687         if(this.hiddenField){
37688             this.hiddenField.value = v;
37689         }
37690         Roo.form.ComboBox.superclass.setValue.call(this, text);
37691         this.value = v;
37692     },
37693     /**
37694      * @property {Object} the last set data for the element
37695      */
37696     
37697     lastData : false,
37698     /**
37699      * Sets the value of the field based on a object which is related to the record format for the store.
37700      * @param {Object} value the value to set as. or false on reset?
37701      */
37702     setFromData : function(o){
37703         var dv = ''; // display value
37704         var vv = ''; // value value..
37705         this.lastData = o;
37706         if (this.displayField) {
37707             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37708         } else {
37709             // this is an error condition!!!
37710             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37711         }
37712         
37713         if(this.valueField){
37714             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37715         }
37716         if(this.hiddenField){
37717             this.hiddenField.value = vv;
37718             
37719             this.lastSelectionText = dv;
37720             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37721             this.value = vv;
37722             return;
37723         }
37724         // no hidden field.. - we store the value in 'value', but still display
37725         // display field!!!!
37726         this.lastSelectionText = dv;
37727         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37728         this.value = vv;
37729         
37730         
37731     },
37732     // private
37733     reset : function(){
37734         // overridden so that last data is reset..
37735         this.setValue(this.originalValue);
37736         this.clearInvalid();
37737         this.lastData = false;
37738     },
37739     // private
37740     findRecord : function(prop, value){
37741         var record;
37742         if(this.store.getCount() > 0){
37743             this.store.each(function(r){
37744                 if(r.data[prop] == value){
37745                     record = r;
37746                     return false;
37747                 }
37748             });
37749         }
37750         return record;
37751     },
37752
37753     // private
37754     onViewMove : function(e, t){
37755         this.inKeyMode = false;
37756     },
37757
37758     // private
37759     onViewOver : function(e, t){
37760         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37761             return;
37762         }
37763         var item = this.view.findItemFromChild(t);
37764         if(item){
37765             var index = this.view.indexOf(item);
37766             this.select(index, false);
37767         }
37768     },
37769
37770     // private
37771     onViewClick : function(doFocus){
37772         var index = this.view.getSelectedIndexes()[0];
37773         var r = this.store.getAt(index);
37774         if(r){
37775             this.onSelect(r, index);
37776         }
37777         if(doFocus !== false && !this.blockFocus){
37778             this.el.focus();
37779         }
37780     },
37781
37782     // private
37783     restrictHeight : function(){
37784         this.innerList.dom.style.height = '';
37785         var inner = this.innerList.dom;
37786         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37787         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37788         this.list.beginUpdate();
37789         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37790         this.list.alignTo(this.el, this.listAlign);
37791         this.list.endUpdate();
37792     },
37793
37794     // private
37795     onEmptyResults : function(){
37796         this.collapse();
37797     },
37798
37799     /**
37800      * Returns true if the dropdown list is expanded, else false.
37801      */
37802     isExpanded : function(){
37803         return this.list.isVisible();
37804     },
37805
37806     /**
37807      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37808      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37809      * @param {String} value The data value of the item to select
37810      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37811      * selected item if it is not currently in view (defaults to true)
37812      * @return {Boolean} True if the value matched an item in the list, else false
37813      */
37814     selectByValue : function(v, scrollIntoView){
37815         if(v !== undefined && v !== null){
37816             var r = this.findRecord(this.valueField || this.displayField, v);
37817             if(r){
37818                 this.select(this.store.indexOf(r), scrollIntoView);
37819                 return true;
37820             }
37821         }
37822         return false;
37823     },
37824
37825     /**
37826      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37827      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37828      * @param {Number} index The zero-based index of the list item to select
37829      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37830      * selected item if it is not currently in view (defaults to true)
37831      */
37832     select : function(index, scrollIntoView){
37833         this.selectedIndex = index;
37834         this.view.select(index);
37835         if(scrollIntoView !== false){
37836             var el = this.view.getNode(index);
37837             if(el){
37838                 this.innerList.scrollChildIntoView(el, false);
37839             }
37840         }
37841     },
37842
37843     // private
37844     selectNext : function(){
37845         var ct = this.store.getCount();
37846         if(ct > 0){
37847             if(this.selectedIndex == -1){
37848                 this.select(0);
37849             }else if(this.selectedIndex < ct-1){
37850                 this.select(this.selectedIndex+1);
37851             }
37852         }
37853     },
37854
37855     // private
37856     selectPrev : function(){
37857         var ct = this.store.getCount();
37858         if(ct > 0){
37859             if(this.selectedIndex == -1){
37860                 this.select(0);
37861             }else if(this.selectedIndex != 0){
37862                 this.select(this.selectedIndex-1);
37863             }
37864         }
37865     },
37866
37867     // private
37868     onKeyUp : function(e){
37869         if(this.editable !== false && !e.isSpecialKey()){
37870             this.lastKey = e.getKey();
37871             this.dqTask.delay(this.queryDelay);
37872         }
37873     },
37874
37875     // private
37876     validateBlur : function(){
37877         return !this.list || !this.list.isVisible();   
37878     },
37879
37880     // private
37881     initQuery : function(){
37882         this.doQuery(this.getRawValue());
37883     },
37884
37885     // private
37886     doForce : function(){
37887         if(this.el.dom.value.length > 0){
37888             this.el.dom.value =
37889                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37890             this.applyEmptyText();
37891         }
37892     },
37893
37894     /**
37895      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37896      * query allowing the query action to be canceled if needed.
37897      * @param {String} query The SQL query to execute
37898      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37899      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37900      * saved in the current store (defaults to false)
37901      */
37902     doQuery : function(q, forceAll){
37903         if(q === undefined || q === null){
37904             q = '';
37905         }
37906         var qe = {
37907             query: q,
37908             forceAll: forceAll,
37909             combo: this,
37910             cancel:false
37911         };
37912         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37913             return false;
37914         }
37915         q = qe.query;
37916         forceAll = qe.forceAll;
37917         if(forceAll === true || (q.length >= this.minChars)){
37918             if(this.lastQuery != q || this.alwaysQuery){
37919                 this.lastQuery = q;
37920                 if(this.mode == 'local'){
37921                     this.selectedIndex = -1;
37922                     if(forceAll){
37923                         this.store.clearFilter();
37924                     }else{
37925                         this.store.filter(this.displayField, q);
37926                     }
37927                     this.onLoad();
37928                 }else{
37929                     this.store.baseParams[this.queryParam] = q;
37930                     this.store.load({
37931                         params: this.getParams(q)
37932                     });
37933                     this.expand();
37934                 }
37935             }else{
37936                 this.selectedIndex = -1;
37937                 this.onLoad();   
37938             }
37939         }
37940     },
37941
37942     // private
37943     getParams : function(q){
37944         var p = {};
37945         //p[this.queryParam] = q;
37946         if(this.pageSize){
37947             p.start = 0;
37948             p.limit = this.pageSize;
37949         }
37950         return p;
37951     },
37952
37953     /**
37954      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37955      */
37956     collapse : function(){
37957         if(!this.isExpanded()){
37958             return;
37959         }
37960         this.list.hide();
37961         Roo.get(document).un('mousedown', this.collapseIf, this);
37962         Roo.get(document).un('mousewheel', this.collapseIf, this);
37963         if (!this.editable) {
37964             Roo.get(document).un('keydown', this.listKeyPress, this);
37965         }
37966         this.fireEvent('collapse', this);
37967     },
37968
37969     // private
37970     collapseIf : function(e){
37971         if(!e.within(this.wrap) && !e.within(this.list)){
37972             this.collapse();
37973         }
37974     },
37975
37976     /**
37977      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37978      */
37979     expand : function(){
37980         if(this.isExpanded() || !this.hasFocus){
37981             return;
37982         }
37983         this.list.alignTo(this.el, this.listAlign);
37984         this.list.show();
37985         Roo.get(document).on('mousedown', this.collapseIf, this);
37986         Roo.get(document).on('mousewheel', this.collapseIf, this);
37987         if (!this.editable) {
37988             Roo.get(document).on('keydown', this.listKeyPress, this);
37989         }
37990         
37991         this.fireEvent('expand', this);
37992     },
37993
37994     // private
37995     // Implements the default empty TriggerField.onTriggerClick function
37996     onTriggerClick : function(){
37997         if(this.disabled){
37998             return;
37999         }
38000         if(this.isExpanded()){
38001             this.collapse();
38002             if (!this.blockFocus) {
38003                 this.el.focus();
38004             }
38005             
38006         }else {
38007             this.hasFocus = true;
38008             if(this.triggerAction == 'all') {
38009                 this.doQuery(this.allQuery, true);
38010             } else {
38011                 this.doQuery(this.getRawValue());
38012             }
38013             if (!this.blockFocus) {
38014                 this.el.focus();
38015             }
38016         }
38017     },
38018     listKeyPress : function(e)
38019     {
38020         //Roo.log('listkeypress');
38021         // scroll to first matching element based on key pres..
38022         if (e.isSpecialKey()) {
38023             return false;
38024         }
38025         var k = String.fromCharCode(e.getKey()).toUpperCase();
38026         //Roo.log(k);
38027         var match  = false;
38028         var csel = this.view.getSelectedNodes();
38029         var cselitem = false;
38030         if (csel.length) {
38031             var ix = this.view.indexOf(csel[0]);
38032             cselitem  = this.store.getAt(ix);
38033             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38034                 cselitem = false;
38035             }
38036             
38037         }
38038         
38039         this.store.each(function(v) { 
38040             if (cselitem) {
38041                 // start at existing selection.
38042                 if (cselitem.id == v.id) {
38043                     cselitem = false;
38044                 }
38045                 return;
38046             }
38047                 
38048             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38049                 match = this.store.indexOf(v);
38050                 return false;
38051             }
38052         }, this);
38053         
38054         if (match === false) {
38055             return true; // no more action?
38056         }
38057         // scroll to?
38058         this.view.select(match);
38059         var sn = Roo.get(this.view.getSelectedNodes()[0])
38060         sn.scrollIntoView(sn.dom.parentNode, false);
38061     }
38062
38063     /** 
38064     * @cfg {Boolean} grow 
38065     * @hide 
38066     */
38067     /** 
38068     * @cfg {Number} growMin 
38069     * @hide 
38070     */
38071     /** 
38072     * @cfg {Number} growMax 
38073     * @hide 
38074     */
38075     /**
38076      * @hide
38077      * @method autoSize
38078      */
38079 });/*
38080  * Based on:
38081  * Ext JS Library 1.1.1
38082  * Copyright(c) 2006-2007, Ext JS, LLC.
38083  *
38084  * Originally Released Under LGPL - original licence link has changed is not relivant.
38085  *
38086  * Fork - LGPL
38087  * <script type="text/javascript">
38088  */
38089 /**
38090  * @class Roo.form.Checkbox
38091  * @extends Roo.form.Field
38092  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38093  * @constructor
38094  * Creates a new Checkbox
38095  * @param {Object} config Configuration options
38096  */
38097 Roo.form.Checkbox = function(config){
38098     Roo.form.Checkbox.superclass.constructor.call(this, config);
38099     this.addEvents({
38100         /**
38101          * @event check
38102          * Fires when the checkbox is checked or unchecked.
38103              * @param {Roo.form.Checkbox} this This checkbox
38104              * @param {Boolean} checked The new checked value
38105              */
38106         check : true
38107     });
38108 };
38109
38110 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38111     /**
38112      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38113      */
38114     focusClass : undefined,
38115     /**
38116      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38117      */
38118     fieldClass: "x-form-field",
38119     /**
38120      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38121      */
38122     checked: false,
38123     /**
38124      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38125      * {tag: "input", type: "checkbox", autocomplete: "off"})
38126      */
38127     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38128     /**
38129      * @cfg {String} boxLabel The text that appears beside the checkbox
38130      */
38131     boxLabel : "",
38132     /**
38133      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38134      */  
38135     inputValue : '1',
38136     /**
38137      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38138      */
38139      valueOff: '0', // value when not checked..
38140
38141     actionMode : 'viewEl', 
38142     //
38143     // private
38144     itemCls : 'x-menu-check-item x-form-item',
38145     groupClass : 'x-menu-group-item',
38146     inputType : 'hidden',
38147     
38148     
38149     inSetChecked: false, // check that we are not calling self...
38150     
38151     inputElement: false, // real input element?
38152     basedOn: false, // ????
38153     
38154     isFormField: true, // not sure where this is needed!!!!
38155
38156     onResize : function(){
38157         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38158         if(!this.boxLabel){
38159             this.el.alignTo(this.wrap, 'c-c');
38160         }
38161     },
38162
38163     initEvents : function(){
38164         Roo.form.Checkbox.superclass.initEvents.call(this);
38165         this.el.on("click", this.onClick,  this);
38166         this.el.on("change", this.onClick,  this);
38167     },
38168
38169
38170     getResizeEl : function(){
38171         return this.wrap;
38172     },
38173
38174     getPositionEl : function(){
38175         return this.wrap;
38176     },
38177
38178     // private
38179     onRender : function(ct, position){
38180         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38181         /*
38182         if(this.inputValue !== undefined){
38183             this.el.dom.value = this.inputValue;
38184         }
38185         */
38186         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38187         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38188         var viewEl = this.wrap.createChild({ 
38189             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38190         this.viewEl = viewEl;   
38191         this.wrap.on('click', this.onClick,  this); 
38192         
38193         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38194         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38195         
38196         
38197         
38198         if(this.boxLabel){
38199             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38200         //    viewEl.on('click', this.onClick,  this); 
38201         }
38202         //if(this.checked){
38203             this.setChecked(this.checked);
38204         //}else{
38205             //this.checked = this.el.dom;
38206         //}
38207
38208     },
38209
38210     // private
38211     initValue : Roo.emptyFn,
38212
38213     /**
38214      * Returns the checked state of the checkbox.
38215      * @return {Boolean} True if checked, else false
38216      */
38217     getValue : function(){
38218         if(this.el){
38219             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38220         }
38221         return this.valueOff;
38222         
38223     },
38224
38225         // private
38226     onClick : function(){ 
38227         this.setChecked(!this.checked);
38228
38229         //if(this.el.dom.checked != this.checked){
38230         //    this.setValue(this.el.dom.checked);
38231        // }
38232     },
38233
38234     /**
38235      * Sets the checked state of the checkbox.
38236      * On is always based on a string comparison between inputValue and the param.
38237      * @param {Boolean/String} value - the value to set 
38238      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38239      */
38240     setValue : function(v,suppressEvent){
38241         
38242         
38243         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38244         //if(this.el && this.el.dom){
38245         //    this.el.dom.checked = this.checked;
38246         //    this.el.dom.defaultChecked = this.checked;
38247         //}
38248         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38249         //this.fireEvent("check", this, this.checked);
38250     },
38251     // private..
38252     setChecked : function(state,suppressEvent)
38253     {
38254         if (this.inSetChecked) {
38255             this.checked = state;
38256             return;
38257         }
38258         
38259     
38260         if(this.wrap){
38261             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38262         }
38263         this.checked = state;
38264         if(suppressEvent !== true){
38265             this.fireEvent('check', this, state);
38266         }
38267         this.inSetChecked = true;
38268         this.el.dom.value = state ? this.inputValue : this.valueOff;
38269         this.inSetChecked = false;
38270         
38271     },
38272     // handle setting of hidden value by some other method!!?!?
38273     setFromHidden: function()
38274     {
38275         if(!this.el){
38276             return;
38277         }
38278         //console.log("SET FROM HIDDEN");
38279         //alert('setFrom hidden');
38280         this.setValue(this.el.dom.value);
38281     },
38282     
38283     onDestroy : function()
38284     {
38285         if(this.viewEl){
38286             Roo.get(this.viewEl).remove();
38287         }
38288          
38289         Roo.form.Checkbox.superclass.onDestroy.call(this);
38290     }
38291
38292 });/*
38293  * Based on:
38294  * Ext JS Library 1.1.1
38295  * Copyright(c) 2006-2007, Ext JS, LLC.
38296  *
38297  * Originally Released Under LGPL - original licence link has changed is not relivant.
38298  *
38299  * Fork - LGPL
38300  * <script type="text/javascript">
38301  */
38302  
38303 /**
38304  * @class Roo.form.Radio
38305  * @extends Roo.form.Checkbox
38306  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38307  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38308  * @constructor
38309  * Creates a new Radio
38310  * @param {Object} config Configuration options
38311  */
38312 Roo.form.Radio = function(){
38313     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38314 };
38315 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38316     inputType: 'radio',
38317
38318     /**
38319      * If this radio is part of a group, it will return the selected value
38320      * @return {String}
38321      */
38322     getGroupValue : function(){
38323         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38324     }
38325 });//<script type="text/javascript">
38326
38327 /*
38328  * Ext JS Library 1.1.1
38329  * Copyright(c) 2006-2007, Ext JS, LLC.
38330  * licensing@extjs.com
38331  * 
38332  * http://www.extjs.com/license
38333  */
38334  
38335  /*
38336   * 
38337   * Known bugs:
38338   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38339   * - IE ? - no idea how much works there.
38340   * 
38341   * 
38342   * 
38343   */
38344  
38345
38346 /**
38347  * @class Ext.form.HtmlEditor
38348  * @extends Ext.form.Field
38349  * Provides a lightweight HTML Editor component.
38350  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38351  * 
38352  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38353  * supported by this editor.</b><br/><br/>
38354  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38355  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38356  */
38357 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38358       /**
38359      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38360      */
38361     toolbars : false,
38362     /**
38363      * @cfg {String} createLinkText The default text for the create link prompt
38364      */
38365     createLinkText : 'Please enter the URL for the link:',
38366     /**
38367      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38368      */
38369     defaultLinkValue : 'http:/'+'/',
38370    
38371     
38372     // id of frame..
38373     frameId: false,
38374     
38375     // private properties
38376     validationEvent : false,
38377     deferHeight: true,
38378     initialized : false,
38379     activated : false,
38380     sourceEditMode : false,
38381     onFocus : Roo.emptyFn,
38382     iframePad:3,
38383     hideMode:'offsets',
38384     defaultAutoCreate : {
38385         tag: "textarea",
38386         style:"width:500px;height:300px;",
38387         autocomplete: "off"
38388     },
38389
38390     // private
38391     initComponent : function(){
38392         this.addEvents({
38393             /**
38394              * @event initialize
38395              * Fires when the editor is fully initialized (including the iframe)
38396              * @param {HtmlEditor} this
38397              */
38398             initialize: true,
38399             /**
38400              * @event activate
38401              * Fires when the editor is first receives the focus. Any insertion must wait
38402              * until after this event.
38403              * @param {HtmlEditor} this
38404              */
38405             activate: true,
38406              /**
38407              * @event beforesync
38408              * Fires before the textarea is updated with content from the editor iframe. Return false
38409              * to cancel the sync.
38410              * @param {HtmlEditor} this
38411              * @param {String} html
38412              */
38413             beforesync: true,
38414              /**
38415              * @event beforepush
38416              * Fires before the iframe editor is updated with content from the textarea. Return false
38417              * to cancel the push.
38418              * @param {HtmlEditor} this
38419              * @param {String} html
38420              */
38421             beforepush: true,
38422              /**
38423              * @event sync
38424              * Fires when the textarea is updated with content from the editor iframe.
38425              * @param {HtmlEditor} this
38426              * @param {String} html
38427              */
38428             sync: true,
38429              /**
38430              * @event push
38431              * Fires when the iframe editor is updated with content from the textarea.
38432              * @param {HtmlEditor} this
38433              * @param {String} html
38434              */
38435             push: true,
38436              /**
38437              * @event editmodechange
38438              * Fires when the editor switches edit modes
38439              * @param {HtmlEditor} this
38440              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38441              */
38442             editmodechange: true,
38443             /**
38444              * @event editorevent
38445              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38446              * @param {HtmlEditor} this
38447              */
38448             editorevent: true
38449         })
38450     },
38451
38452     /**
38453      * Protected method that will not generally be called directly. It
38454      * is called when the editor creates its toolbar. Override this method if you need to
38455      * add custom toolbar buttons.
38456      * @param {HtmlEditor} editor
38457      */
38458     createToolbar : function(editor){
38459         if (!editor.toolbars || !editor.toolbars.length) {
38460             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38461         }
38462         
38463         for (var i =0 ; i < editor.toolbars.length;i++) {
38464             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38465             editor.toolbars[i].init(editor);
38466         }
38467          
38468         
38469     },
38470
38471     /**
38472      * Protected method that will not generally be called directly. It
38473      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38474      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38475      */
38476     getDocMarkup : function(){
38477         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38478     },
38479
38480     // private
38481     onRender : function(ct, position){
38482         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38483         this.el.dom.style.border = '0 none';
38484         this.el.dom.setAttribute('tabIndex', -1);
38485         this.el.addClass('x-hidden');
38486         if(Roo.isIE){ // fix IE 1px bogus margin
38487             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38488         }
38489         this.wrap = this.el.wrap({
38490             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38491         });
38492
38493         this.frameId = Roo.id();
38494         this.createToolbar(this);
38495         
38496         
38497         
38498         
38499       
38500         
38501         var iframe = this.wrap.createChild({
38502             tag: 'iframe',
38503             id: this.frameId,
38504             name: this.frameId,
38505             frameBorder : 'no',
38506             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38507         });
38508         
38509        // console.log(iframe);
38510         //this.wrap.dom.appendChild(iframe);
38511
38512         this.iframe = iframe.dom;
38513
38514          this.assignDocWin();
38515         
38516         this.doc.designMode = 'on';
38517        
38518         this.doc.open();
38519         this.doc.write(this.getDocMarkup());
38520         this.doc.close();
38521
38522         
38523         var task = { // must defer to wait for browser to be ready
38524             run : function(){
38525                 //console.log("run task?" + this.doc.readyState);
38526                 this.assignDocWin();
38527                 if(this.doc.body || this.doc.readyState == 'complete'){
38528                     try {
38529                         this.doc.designMode="on";
38530                     } catch (e) {
38531                         return;
38532                     }
38533                     Roo.TaskMgr.stop(task);
38534                     this.initEditor.defer(10, this);
38535                 }
38536             },
38537             interval : 10,
38538             duration:10000,
38539             scope: this
38540         };
38541         Roo.TaskMgr.start(task);
38542
38543         if(!this.width){
38544             this.setSize(this.el.getSize());
38545         }
38546     },
38547
38548     // private
38549     onResize : function(w, h){
38550         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38551         if(this.el && this.iframe){
38552             if(typeof w == 'number'){
38553                 var aw = w - this.wrap.getFrameWidth('lr');
38554                 this.el.setWidth(this.adjustWidth('textarea', aw));
38555                 this.iframe.style.width = aw + 'px';
38556             }
38557             if(typeof h == 'number'){
38558                 var tbh = 0;
38559                 for (var i =0; i < this.toolbars.length;i++) {
38560                     // fixme - ask toolbars for heights?
38561                     tbh += this.toolbars[i].tb.el.getHeight();
38562                 }
38563                 
38564                 
38565                 
38566                 
38567                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38568                 this.el.setHeight(this.adjustWidth('textarea', ah));
38569                 this.iframe.style.height = ah + 'px';
38570                 if(this.doc){
38571                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38572                 }
38573             }
38574         }
38575     },
38576
38577     /**
38578      * Toggles the editor between standard and source edit mode.
38579      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38580      */
38581     toggleSourceEdit : function(sourceEditMode){
38582         
38583         this.sourceEditMode = sourceEditMode === true;
38584         
38585         if(this.sourceEditMode){
38586           
38587             this.syncValue();
38588             this.iframe.className = 'x-hidden';
38589             this.el.removeClass('x-hidden');
38590             this.el.dom.removeAttribute('tabIndex');
38591             this.el.focus();
38592         }else{
38593              
38594             this.pushValue();
38595             this.iframe.className = '';
38596             this.el.addClass('x-hidden');
38597             this.el.dom.setAttribute('tabIndex', -1);
38598             this.deferFocus();
38599         }
38600         this.setSize(this.wrap.getSize());
38601         this.fireEvent('editmodechange', this, this.sourceEditMode);
38602     },
38603
38604     // private used internally
38605     createLink : function(){
38606         var url = prompt(this.createLinkText, this.defaultLinkValue);
38607         if(url && url != 'http:/'+'/'){
38608             this.relayCmd('createlink', url);
38609         }
38610     },
38611
38612     // private (for BoxComponent)
38613     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38614
38615     // private (for BoxComponent)
38616     getResizeEl : function(){
38617         return this.wrap;
38618     },
38619
38620     // private (for BoxComponent)
38621     getPositionEl : function(){
38622         return this.wrap;
38623     },
38624
38625     // private
38626     initEvents : function(){
38627         this.originalValue = this.getValue();
38628     },
38629
38630     /**
38631      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38632      * @method
38633      */
38634     markInvalid : Roo.emptyFn,
38635     /**
38636      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38637      * @method
38638      */
38639     clearInvalid : Roo.emptyFn,
38640
38641     setValue : function(v){
38642         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38643         this.pushValue();
38644     },
38645
38646     /**
38647      * Protected method that will not generally be called directly. If you need/want
38648      * custom HTML cleanup, this is the method you should override.
38649      * @param {String} html The HTML to be cleaned
38650      * return {String} The cleaned HTML
38651      */
38652     cleanHtml : function(html){
38653         html = String(html);
38654         if(html.length > 5){
38655             if(Roo.isSafari){ // strip safari nonsense
38656                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38657             }
38658         }
38659         if(html == '&nbsp;'){
38660             html = '';
38661         }
38662         return html;
38663     },
38664
38665     /**
38666      * Protected method that will not generally be called directly. Syncs the contents
38667      * of the editor iframe with the textarea.
38668      */
38669     syncValue : function(){
38670         if(this.initialized){
38671             var bd = (this.doc.body || this.doc.documentElement);
38672             this.cleanUpPaste();
38673             var html = bd.innerHTML;
38674             if(Roo.isSafari){
38675                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38676                 var m = bs.match(/text-align:(.*?);/i);
38677                 if(m && m[1]){
38678                     html = '<div style="'+m[0]+'">' + html + '</div>';
38679                 }
38680             }
38681             html = this.cleanHtml(html);
38682             if(this.fireEvent('beforesync', this, html) !== false){
38683                 this.el.dom.value = html;
38684                 this.fireEvent('sync', this, html);
38685             }
38686         }
38687     },
38688
38689     /**
38690      * Protected method that will not generally be called directly. Pushes the value of the textarea
38691      * into the iframe editor.
38692      */
38693     pushValue : function(){
38694         if(this.initialized){
38695             var v = this.el.dom.value;
38696             if(v.length < 1){
38697                 v = '&#160;';
38698             }
38699             
38700             if(this.fireEvent('beforepush', this, v) !== false){
38701                 var d = (this.doc.body || this.doc.documentElement);
38702                 d.innerHTML = v;
38703                 this.cleanUpPaste();
38704                 this.el.dom.value = d.innerHTML;
38705                 this.fireEvent('push', this, v);
38706             }
38707         }
38708     },
38709
38710     // private
38711     deferFocus : function(){
38712         this.focus.defer(10, this);
38713     },
38714
38715     // doc'ed in Field
38716     focus : function(){
38717         if(this.win && !this.sourceEditMode){
38718             this.win.focus();
38719         }else{
38720             this.el.focus();
38721         }
38722     },
38723     
38724     assignDocWin: function()
38725     {
38726         var iframe = this.iframe;
38727         
38728          if(Roo.isIE){
38729             this.doc = iframe.contentWindow.document;
38730             this.win = iframe.contentWindow;
38731         } else {
38732             if (!Roo.get(this.frameId)) {
38733                 return;
38734             }
38735             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38736             this.win = Roo.get(this.frameId).dom.contentWindow;
38737         }
38738     },
38739     
38740     // private
38741     initEditor : function(){
38742         //console.log("INIT EDITOR");
38743         this.assignDocWin();
38744         
38745         
38746         
38747         this.doc.designMode="on";
38748         this.doc.open();
38749         this.doc.write(this.getDocMarkup());
38750         this.doc.close();
38751         
38752         var dbody = (this.doc.body || this.doc.documentElement);
38753         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38754         // this copies styles from the containing element into thsi one..
38755         // not sure why we need all of this..
38756         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38757         ss['background-attachment'] = 'fixed'; // w3c
38758         dbody.bgProperties = 'fixed'; // ie
38759         Roo.DomHelper.applyStyles(dbody, ss);
38760         Roo.EventManager.on(this.doc, {
38761             'mousedown': this.onEditorEvent,
38762             'dblclick': this.onEditorEvent,
38763             'click': this.onEditorEvent,
38764             'keyup': this.onEditorEvent,
38765             buffer:100,
38766             scope: this
38767         });
38768         if(Roo.isGecko){
38769             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38770         }
38771         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38772             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38773         }
38774         this.initialized = true;
38775
38776         this.fireEvent('initialize', this);
38777         this.pushValue();
38778     },
38779
38780     // private
38781     onDestroy : function(){
38782         
38783         
38784         
38785         if(this.rendered){
38786             
38787             for (var i =0; i < this.toolbars.length;i++) {
38788                 // fixme - ask toolbars for heights?
38789                 this.toolbars[i].onDestroy();
38790             }
38791             
38792             this.wrap.dom.innerHTML = '';
38793             this.wrap.remove();
38794         }
38795     },
38796
38797     // private
38798     onFirstFocus : function(){
38799         
38800         this.assignDocWin();
38801         
38802         
38803         this.activated = true;
38804         for (var i =0; i < this.toolbars.length;i++) {
38805             this.toolbars[i].onFirstFocus();
38806         }
38807        
38808         if(Roo.isGecko){ // prevent silly gecko errors
38809             this.win.focus();
38810             var s = this.win.getSelection();
38811             if(!s.focusNode || s.focusNode.nodeType != 3){
38812                 var r = s.getRangeAt(0);
38813                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38814                 r.collapse(true);
38815                 this.deferFocus();
38816             }
38817             try{
38818                 this.execCmd('useCSS', true);
38819                 this.execCmd('styleWithCSS', false);
38820             }catch(e){}
38821         }
38822         this.fireEvent('activate', this);
38823     },
38824
38825     // private
38826     adjustFont: function(btn){
38827         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38828         //if(Roo.isSafari){ // safari
38829         //    adjust *= 2;
38830        // }
38831         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38832         if(Roo.isSafari){ // safari
38833             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38834             v =  (v < 10) ? 10 : v;
38835             v =  (v > 48) ? 48 : v;
38836             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38837             
38838         }
38839         
38840         
38841         v = Math.max(1, v+adjust);
38842         
38843         this.execCmd('FontSize', v  );
38844     },
38845
38846     onEditorEvent : function(e){
38847         this.fireEvent('editorevent', this, e);
38848       //  this.updateToolbar();
38849         this.syncValue();
38850     },
38851
38852     insertTag : function(tg)
38853     {
38854         // could be a bit smarter... -> wrap the current selected tRoo..
38855         
38856         this.execCmd("formatblock",   tg);
38857         
38858     },
38859     
38860     insertText : function(txt)
38861     {
38862         
38863         
38864         range = this.createRange();
38865         range.deleteContents();
38866                //alert(Sender.getAttribute('label'));
38867                
38868         range.insertNode(this.doc.createTextNode(txt));
38869     } ,
38870     
38871     // private
38872     relayBtnCmd : function(btn){
38873         this.relayCmd(btn.cmd);
38874     },
38875
38876     /**
38877      * Executes a Midas editor command on the editor document and performs necessary focus and
38878      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38879      * @param {String} cmd The Midas command
38880      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38881      */
38882     relayCmd : function(cmd, value){
38883         this.win.focus();
38884         this.execCmd(cmd, value);
38885         this.fireEvent('editorevent', this);
38886         //this.updateToolbar();
38887         this.deferFocus();
38888     },
38889
38890     /**
38891      * Executes a Midas editor command directly on the editor document.
38892      * For visual commands, you should use {@link #relayCmd} instead.
38893      * <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     execCmd : function(cmd, value){
38898         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38899         this.syncValue();
38900     },
38901
38902    
38903     /**
38904      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38905      * to insert tRoo.
38906      * @param {String} text
38907      */
38908     insertAtCursor : function(text){
38909         if(!this.activated){
38910             return;
38911         }
38912         if(Roo.isIE){
38913             this.win.focus();
38914             var r = this.doc.selection.createRange();
38915             if(r){
38916                 r.collapse(true);
38917                 r.pasteHTML(text);
38918                 this.syncValue();
38919                 this.deferFocus();
38920             }
38921         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38922             this.win.focus();
38923             this.execCmd('InsertHTML', text);
38924             this.deferFocus();
38925         }
38926     },
38927  // private
38928     mozKeyPress : function(e){
38929         if(e.ctrlKey){
38930             var c = e.getCharCode(), cmd;
38931           
38932             if(c > 0){
38933                 c = String.fromCharCode(c).toLowerCase();
38934                 switch(c){
38935                     case 'b':
38936                         cmd = 'bold';
38937                     break;
38938                     case 'i':
38939                         cmd = 'italic';
38940                     break;
38941                     case 'u':
38942                         cmd = 'underline';
38943                     case 'v':
38944                         this.cleanUpPaste.defer(100, this);
38945                         return;
38946                     break;
38947                 }
38948                 if(cmd){
38949                     this.win.focus();
38950                     this.execCmd(cmd);
38951                     this.deferFocus();
38952                     e.preventDefault();
38953                 }
38954                 
38955             }
38956         }
38957     },
38958
38959     // private
38960     fixKeys : function(){ // load time branching for fastest keydown performance
38961         if(Roo.isIE){
38962             return function(e){
38963                 var k = e.getKey(), r;
38964                 if(k == e.TAB){
38965                     e.stopEvent();
38966                     r = this.doc.selection.createRange();
38967                     if(r){
38968                         r.collapse(true);
38969                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38970                         this.deferFocus();
38971                     }
38972                     return;
38973                 }
38974                 
38975                 if(k == e.ENTER){
38976                     r = this.doc.selection.createRange();
38977                     if(r){
38978                         var target = r.parentElement();
38979                         if(!target || target.tagName.toLowerCase() != 'li'){
38980                             e.stopEvent();
38981                             r.pasteHTML('<br />');
38982                             r.collapse(false);
38983                             r.select();
38984                         }
38985                     }
38986                 }
38987                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38988                     this.cleanUpPaste.defer(100, this);
38989                     return;
38990                 }
38991                 
38992                 
38993             };
38994         }else if(Roo.isOpera){
38995             return function(e){
38996                 var k = e.getKey();
38997                 if(k == e.TAB){
38998                     e.stopEvent();
38999                     this.win.focus();
39000                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39001                     this.deferFocus();
39002                 }
39003                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39004                     this.cleanUpPaste.defer(100, this);
39005                     return;
39006                 }
39007                 
39008             };
39009         }else if(Roo.isSafari){
39010             return function(e){
39011                 var k = e.getKey();
39012                 
39013                 if(k == e.TAB){
39014                     e.stopEvent();
39015                     this.execCmd('InsertText','\t');
39016                     this.deferFocus();
39017                     return;
39018                 }
39019                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39020                     this.cleanUpPaste.defer(100, this);
39021                     return;
39022                 }
39023                 
39024              };
39025         }
39026     }(),
39027     
39028     getAllAncestors: function()
39029     {
39030         var p = this.getSelectedNode();
39031         var a = [];
39032         if (!p) {
39033             a.push(p); // push blank onto stack..
39034             p = this.getParentElement();
39035         }
39036         
39037         
39038         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39039             a.push(p);
39040             p = p.parentNode;
39041         }
39042         a.push(this.doc.body);
39043         return a;
39044     },
39045     lastSel : false,
39046     lastSelNode : false,
39047     
39048     
39049     getSelection : function() 
39050     {
39051         this.assignDocWin();
39052         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39053     },
39054     
39055     getSelectedNode: function() 
39056     {
39057         // this may only work on Gecko!!!
39058         
39059         // should we cache this!!!!
39060         
39061         
39062         
39063          
39064         var range = this.createRange(this.getSelection());
39065         
39066         if (Roo.isIE) {
39067             var parent = range.parentElement();
39068             while (true) {
39069                 var testRange = range.duplicate();
39070                 testRange.moveToElementText(parent);
39071                 if (testRange.inRange(range)) {
39072                     break;
39073                 }
39074                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39075                     break;
39076                 }
39077                 parent = parent.parentElement;
39078             }
39079             return parent;
39080         }
39081         
39082         
39083         var ar = range.endContainer.childNodes;
39084         if (!ar.length) {
39085             ar = range.commonAncestorContainer.childNodes;
39086             //alert(ar.length);
39087         }
39088         var nodes = [];
39089         var other_nodes = [];
39090         var has_other_nodes = false;
39091         for (var i=0;i<ar.length;i++) {
39092             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39093                 continue;
39094             }
39095             // fullly contained node.
39096             
39097             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39098                 nodes.push(ar[i]);
39099                 continue;
39100             }
39101             
39102             // probably selected..
39103             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39104                 other_nodes.push(ar[i]);
39105                 continue;
39106             }
39107             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39108                 continue;
39109             }
39110             
39111             
39112             has_other_nodes = true;
39113         }
39114         if (!nodes.length && other_nodes.length) {
39115             nodes= other_nodes;
39116         }
39117         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39118             return false;
39119         }
39120         
39121         return nodes[0];
39122     },
39123     createRange: function(sel)
39124     {
39125         // this has strange effects when using with 
39126         // top toolbar - not sure if it's a great idea.
39127         //this.editor.contentWindow.focus();
39128         if (typeof sel != "undefined") {
39129             try {
39130                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39131             } catch(e) {
39132                 return this.doc.createRange();
39133             }
39134         } else {
39135             return this.doc.createRange();
39136         }
39137     },
39138     getParentElement: function()
39139     {
39140         
39141         this.assignDocWin();
39142         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39143         
39144         var range = this.createRange(sel);
39145          
39146         try {
39147             var p = range.commonAncestorContainer;
39148             while (p.nodeType == 3) { // text node
39149                 p = p.parentNode;
39150             }
39151             return p;
39152         } catch (e) {
39153             return null;
39154         }
39155     
39156     },
39157     
39158     
39159     
39160     // BC Hacks - cause I cant work out what i was trying to do..
39161     rangeIntersectsNode : function(range, node)
39162     {
39163         var nodeRange = node.ownerDocument.createRange();
39164         try {
39165             nodeRange.selectNode(node);
39166         }
39167         catch (e) {
39168             nodeRange.selectNodeContents(node);
39169         }
39170
39171         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39172                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39173     },
39174     rangeCompareNode : function(range, node) {
39175         var nodeRange = node.ownerDocument.createRange();
39176         try {
39177             nodeRange.selectNode(node);
39178         } catch (e) {
39179             nodeRange.selectNodeContents(node);
39180         }
39181         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39182         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39183
39184         if (nodeIsBefore && !nodeIsAfter)
39185             return 0;
39186         if (!nodeIsBefore && nodeIsAfter)
39187             return 1;
39188         if (nodeIsBefore && nodeIsAfter)
39189             return 2;
39190
39191         return 3;
39192     },
39193
39194     // private? - in a new class?
39195     cleanUpPaste :  function()
39196     {
39197         // cleans up the whole document..
39198       //  console.log('cleanuppaste');
39199         this.cleanUpChildren(this.doc.body);
39200         
39201         
39202     },
39203     cleanUpChildren : function (n)
39204     {
39205         if (!n.childNodes.length) {
39206             return;
39207         }
39208         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39209            this.cleanUpChild(n.childNodes[i]);
39210         }
39211     },
39212     
39213     
39214         
39215     
39216     cleanUpChild : function (node)
39217     {
39218         //console.log(node);
39219         if (node.nodeName == "#text") {
39220             // clean up silly Windows -- stuff?
39221             return; 
39222         }
39223         if (node.nodeName == "#comment") {
39224             node.parentNode.removeChild(node);
39225             // clean up silly Windows -- stuff?
39226             return; 
39227         }
39228         
39229         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39230             // remove node.
39231             node.parentNode.removeChild(node);
39232             return;
39233             
39234         }
39235         if (!node.attributes || !node.attributes.length) {
39236             this.cleanUpChildren(node);
39237             return;
39238         }
39239         
39240         function cleanAttr(n,v)
39241         {
39242             
39243             if (v.match(/^\./) || v.match(/^\//)) {
39244                 return;
39245             }
39246             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39247                 return;
39248             }
39249             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39250             node.removeAttribute(n);
39251             
39252         }
39253         
39254         function cleanStyle(n,v)
39255         {
39256             if (v.match(/expression/)) { //XSS?? should we even bother..
39257                 node.removeAttribute(n);
39258                 return;
39259             }
39260             
39261             
39262             var parts = v.split(/;/);
39263             Roo.each(parts, function(p) {
39264                 p = p.replace(/\s+/g,'');
39265                 if (!p.length) {
39266                     return;
39267                 }
39268                 var l = p.split(':').shift().replace(/\s+/g,'');
39269                 
39270                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39271                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39272                     node.removeAttribute(n);
39273                     return false;
39274                 }
39275             });
39276             
39277             
39278         }
39279         
39280         
39281         for (var i = node.attributes.length-1; i > -1 ; i--) {
39282             var a = node.attributes[i];
39283             //console.log(a);
39284             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39285                 node.removeAttribute(a.name);
39286                 return;
39287             }
39288             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39289                 cleanAttr(a.name,a.value); // fixme..
39290                 return;
39291             }
39292             if (a.name == 'style') {
39293                 cleanStyle(a.name,a.value);
39294             }
39295             /// clean up MS crap..
39296             if (a.name == 'class') {
39297                 if (a.value.match(/^Mso/)) {
39298                     node.className = '';
39299                 }
39300             }
39301             
39302             // style cleanup!?
39303             // class cleanup?
39304             
39305         }
39306         
39307         
39308         this.cleanUpChildren(node);
39309         
39310         
39311     }
39312     
39313     
39314     // hide stuff that is not compatible
39315     /**
39316      * @event blur
39317      * @hide
39318      */
39319     /**
39320      * @event change
39321      * @hide
39322      */
39323     /**
39324      * @event focus
39325      * @hide
39326      */
39327     /**
39328      * @event specialkey
39329      * @hide
39330      */
39331     /**
39332      * @cfg {String} fieldClass @hide
39333      */
39334     /**
39335      * @cfg {String} focusClass @hide
39336      */
39337     /**
39338      * @cfg {String} autoCreate @hide
39339      */
39340     /**
39341      * @cfg {String} inputType @hide
39342      */
39343     /**
39344      * @cfg {String} invalidClass @hide
39345      */
39346     /**
39347      * @cfg {String} invalidText @hide
39348      */
39349     /**
39350      * @cfg {String} msgFx @hide
39351      */
39352     /**
39353      * @cfg {String} validateOnBlur @hide
39354      */
39355 });
39356
39357 Roo.form.HtmlEditor.white = [
39358         'area', 'br', 'img', 'input', 'hr', 'wbr',
39359         
39360        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39361        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39362        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39363        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39364        'table',   'ul',         'xmp', 
39365        
39366        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39367       'thead',   'tr', 
39368      
39369       'dir', 'menu', 'ol', 'ul', 'dl',
39370        
39371       'embed',  'object'
39372 ];
39373
39374
39375 Roo.form.HtmlEditor.black = [
39376     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39377         'applet', // 
39378         'base',   'basefont', 'bgsound', 'blink',  'body', 
39379         'frame',  'frameset', 'head',    'html',   'ilayer', 
39380         'iframe', 'layer',  'link',     'meta',    'object',   
39381         'script', 'style' ,'title',  'xml' // clean later..
39382 ];
39383 Roo.form.HtmlEditor.clean = [
39384     'script', 'style', 'title', 'xml'
39385 ];
39386
39387 // attributes..
39388
39389 Roo.form.HtmlEditor.ablack = [
39390     'on'
39391 ];
39392     
39393 Roo.form.HtmlEditor.aclean = [ 
39394     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39395 ];
39396
39397 // protocols..
39398 Roo.form.HtmlEditor.pwhite= [
39399         'http',  'https',  'mailto'
39400 ];
39401
39402 Roo.form.HtmlEditor.cwhite= [
39403         'text-align',
39404         'font-size'
39405 ];
39406
39407 // <script type="text/javascript">
39408 /*
39409  * Based on
39410  * Ext JS Library 1.1.1
39411  * Copyright(c) 2006-2007, Ext JS, LLC.
39412  *  
39413  
39414  */
39415
39416 /**
39417  * @class Roo.form.HtmlEditorToolbar1
39418  * Basic Toolbar
39419  * 
39420  * Usage:
39421  *
39422  new Roo.form.HtmlEditor({
39423     ....
39424     toolbars : [
39425         new Roo.form.HtmlEditorToolbar1({
39426             disable : { fonts: 1 , format: 1, ..., ... , ...],
39427             btns : [ .... ]
39428         })
39429     }
39430      
39431  * 
39432  * @cfg {Object} disable List of elements to disable..
39433  * @cfg {Array} btns List of additional buttons.
39434  * 
39435  * 
39436  * NEEDS Extra CSS? 
39437  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39438  */
39439  
39440 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39441 {
39442     
39443     Roo.apply(this, config);
39444     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39445     // dont call parent... till later.
39446 }
39447
39448 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39449     
39450     tb: false,
39451     
39452     rendered: false,
39453     
39454     editor : false,
39455     /**
39456      * @cfg {Object} disable  List of toolbar elements to disable
39457          
39458      */
39459     disable : false,
39460       /**
39461      * @cfg {Array} fontFamilies An array of available font families
39462      */
39463     fontFamilies : [
39464         'Arial',
39465         'Courier New',
39466         'Tahoma',
39467         'Times New Roman',
39468         'Verdana'
39469     ],
39470     
39471     specialChars : [
39472            "&#169;",
39473           "&#174;",     
39474           "&#8482;",    
39475           "&#163;" ,    
39476          // "&#8212;",    
39477           "&#8230;",    
39478           "&#247;" ,    
39479         //  "&#225;" ,     ?? a acute?
39480            "&#8364;"    , //Euro
39481        //   "&#8220;"    ,
39482         //  "&#8221;"    ,
39483         //  "&#8226;"    ,
39484           "&#176;"  //   , // degrees
39485
39486          // "&#233;"     , // e ecute
39487          // "&#250;"     , // u ecute?
39488     ],
39489     inputElements : [ 
39490             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39491             "input:submit", "input:button", "select", "textarea", "label" ],
39492     formats : [
39493         ["p"] ,  
39494         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39495         ["pre"],[ "code"], 
39496         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39497     ],
39498      /**
39499      * @cfg {String} defaultFont default font to use.
39500      */
39501     defaultFont: 'tahoma',
39502    
39503     fontSelect : false,
39504     
39505     
39506     formatCombo : false,
39507     
39508     init : function(editor)
39509     {
39510         this.editor = editor;
39511         
39512         
39513         var fid = editor.frameId;
39514         var etb = this;
39515         function btn(id, toggle, handler){
39516             var xid = fid + '-'+ id ;
39517             return {
39518                 id : xid,
39519                 cmd : id,
39520                 cls : 'x-btn-icon x-edit-'+id,
39521                 enableToggle:toggle !== false,
39522                 scope: editor, // was editor...
39523                 handler:handler||editor.relayBtnCmd,
39524                 clickEvent:'mousedown',
39525                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39526                 tabIndex:-1
39527             };
39528         }
39529         
39530         
39531         
39532         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39533         this.tb = tb;
39534          // stop form submits
39535         tb.el.on('click', function(e){
39536             e.preventDefault(); // what does this do?
39537         });
39538
39539         if(!this.disable.font && !Roo.isSafari){
39540             /* why no safari for fonts
39541             editor.fontSelect = tb.el.createChild({
39542                 tag:'select',
39543                 tabIndex: -1,
39544                 cls:'x-font-select',
39545                 html: editor.createFontOptions()
39546             });
39547             editor.fontSelect.on('change', function(){
39548                 var font = editor.fontSelect.dom.value;
39549                 editor.relayCmd('fontname', font);
39550                 editor.deferFocus();
39551             }, editor);
39552             tb.add(
39553                 editor.fontSelect.dom,
39554                 '-'
39555             );
39556             */
39557         };
39558         if(!this.disable.formats){
39559             this.formatCombo = new Roo.form.ComboBox({
39560                 store: new Roo.data.SimpleStore({
39561                     id : 'tag',
39562                     fields: ['tag'],
39563                     data : this.formats // from states.js
39564                 }),
39565                 blockFocus : true,
39566                 //autoCreate : {tag: "div",  size: "20"},
39567                 displayField:'tag',
39568                 typeAhead: false,
39569                 mode: 'local',
39570                 editable : false,
39571                 triggerAction: 'all',
39572                 emptyText:'Add tag',
39573                 selectOnFocus:true,
39574                 width:135,
39575                 listeners : {
39576                     'select': function(c, r, i) {
39577                         editor.insertTag(r.get('tag'));
39578                         editor.focus();
39579                     }
39580                 }
39581
39582             });
39583             tb.addField(this.formatCombo);
39584             
39585         }
39586         
39587         if(!this.disable.format){
39588             tb.add(
39589                 btn('bold'),
39590                 btn('italic'),
39591                 btn('underline')
39592             );
39593         };
39594         if(!this.disable.fontSize){
39595             tb.add(
39596                 '-',
39597                 
39598                 
39599                 btn('increasefontsize', false, editor.adjustFont),
39600                 btn('decreasefontsize', false, editor.adjustFont)
39601             );
39602         };
39603         
39604         
39605         if(this.disable.colors){
39606             tb.add(
39607                 '-', {
39608                     id:editor.frameId +'-forecolor',
39609                     cls:'x-btn-icon x-edit-forecolor',
39610                     clickEvent:'mousedown',
39611                     tooltip: this.buttonTips['forecolor'] || undefined,
39612                     tabIndex:-1,
39613                     menu : new Roo.menu.ColorMenu({
39614                         allowReselect: true,
39615                         focus: Roo.emptyFn,
39616                         value:'000000',
39617                         plain:true,
39618                         selectHandler: function(cp, color){
39619                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39620                             editor.deferFocus();
39621                         },
39622                         scope: editor,
39623                         clickEvent:'mousedown'
39624                     })
39625                 }, {
39626                     id:editor.frameId +'backcolor',
39627                     cls:'x-btn-icon x-edit-backcolor',
39628                     clickEvent:'mousedown',
39629                     tooltip: this.buttonTips['backcolor'] || undefined,
39630                     tabIndex:-1,
39631                     menu : new Roo.menu.ColorMenu({
39632                         focus: Roo.emptyFn,
39633                         value:'FFFFFF',
39634                         plain:true,
39635                         allowReselect: true,
39636                         selectHandler: function(cp, color){
39637                             if(Roo.isGecko){
39638                                 editor.execCmd('useCSS', false);
39639                                 editor.execCmd('hilitecolor', color);
39640                                 editor.execCmd('useCSS', true);
39641                                 editor.deferFocus();
39642                             }else{
39643                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39644                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39645                                 editor.deferFocus();
39646                             }
39647                         },
39648                         scope:editor,
39649                         clickEvent:'mousedown'
39650                     })
39651                 }
39652             );
39653         };
39654         // now add all the items...
39655         
39656
39657         if(!this.disable.alignments){
39658             tb.add(
39659                 '-',
39660                 btn('justifyleft'),
39661                 btn('justifycenter'),
39662                 btn('justifyright')
39663             );
39664         };
39665
39666         //if(!Roo.isSafari){
39667             if(!this.disable.links){
39668                 tb.add(
39669                     '-',
39670                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39671                 );
39672             };
39673
39674             if(!this.disable.lists){
39675                 tb.add(
39676                     '-',
39677                     btn('insertorderedlist'),
39678                     btn('insertunorderedlist')
39679                 );
39680             }
39681             if(!this.disable.sourceEdit){
39682                 tb.add(
39683                     '-',
39684                     btn('sourceedit', true, function(btn){
39685                         this.toggleSourceEdit(btn.pressed);
39686                     })
39687                 );
39688             }
39689         //}
39690         
39691         var smenu = { };
39692         // special menu.. - needs to be tidied up..
39693         if (!this.disable.special) {
39694             smenu = {
39695                 text: "&#169;",
39696                 cls: 'x-edit-none',
39697                 menu : {
39698                     items : []
39699                    }
39700             };
39701             for (var i =0; i < this.specialChars.length; i++) {
39702                 smenu.menu.items.push({
39703                     
39704                     html: this.specialChars[i],
39705                     handler: function(a,b) {
39706                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39707                         
39708                     },
39709                     tabIndex:-1
39710                 });
39711             }
39712             
39713             
39714             tb.add(smenu);
39715             
39716             
39717         }
39718         if (this.btns) {
39719             for(var i =0; i< this.btns.length;i++) {
39720                 var b = this.btns[i];
39721                 b.cls =  'x-edit-none';
39722                 b.scope = editor;
39723                 tb.add(b);
39724             }
39725         
39726         }
39727         
39728         
39729         
39730         // disable everything...
39731         
39732         this.tb.items.each(function(item){
39733            if(item.id != editor.frameId+ '-sourceedit'){
39734                 item.disable();
39735             }
39736         });
39737         this.rendered = true;
39738         
39739         // the all the btns;
39740         editor.on('editorevent', this.updateToolbar, this);
39741         // other toolbars need to implement this..
39742         //editor.on('editmodechange', this.updateToolbar, this);
39743     },
39744     
39745     
39746     
39747     /**
39748      * Protected method that will not generally be called directly. It triggers
39749      * a toolbar update by reading the markup state of the current selection in the editor.
39750      */
39751     updateToolbar: function(){
39752
39753         if(!this.editor.activated){
39754             this.editor.onFirstFocus();
39755             return;
39756         }
39757
39758         var btns = this.tb.items.map, 
39759             doc = this.editor.doc,
39760             frameId = this.editor.frameId;
39761
39762         if(!this.disable.font && !Roo.isSafari){
39763             /*
39764             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39765             if(name != this.fontSelect.dom.value){
39766                 this.fontSelect.dom.value = name;
39767             }
39768             */
39769         }
39770         if(!this.disable.format){
39771             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39772             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39773             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39774         }
39775         if(!this.disable.alignments){
39776             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39777             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39778             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39779         }
39780         if(!Roo.isSafari && !this.disable.lists){
39781             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39782             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39783         }
39784         
39785         var ans = this.editor.getAllAncestors();
39786         if (this.formatCombo) {
39787             
39788             
39789             var store = this.formatCombo.store;
39790             this.formatCombo.setValue("");
39791             for (var i =0; i < ans.length;i++) {
39792                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39793                     // select it..
39794                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39795                     break;
39796                 }
39797             }
39798         }
39799         
39800         
39801         
39802         // hides menus... - so this cant be on a menu...
39803         Roo.menu.MenuMgr.hideAll();
39804
39805         //this.editorsyncValue();
39806     },
39807    
39808     
39809     createFontOptions : function(){
39810         var buf = [], fs = this.fontFamilies, ff, lc;
39811         for(var i = 0, len = fs.length; i< len; i++){
39812             ff = fs[i];
39813             lc = ff.toLowerCase();
39814             buf.push(
39815                 '<option value="',lc,'" style="font-family:',ff,';"',
39816                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39817                     ff,
39818                 '</option>'
39819             );
39820         }
39821         return buf.join('');
39822     },
39823     
39824     toggleSourceEdit : function(sourceEditMode){
39825         if(sourceEditMode === undefined){
39826             sourceEditMode = !this.sourceEditMode;
39827         }
39828         this.sourceEditMode = sourceEditMode === true;
39829         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39830         // just toggle the button?
39831         if(btn.pressed !== this.editor.sourceEditMode){
39832             btn.toggle(this.editor.sourceEditMode);
39833             return;
39834         }
39835         
39836         if(this.sourceEditMode){
39837             this.tb.items.each(function(item){
39838                 if(item.cmd != 'sourceedit'){
39839                     item.disable();
39840                 }
39841             });
39842           
39843         }else{
39844             if(this.initialized){
39845                 this.tb.items.each(function(item){
39846                     item.enable();
39847                 });
39848             }
39849             
39850         }
39851         // tell the editor that it's been pressed..
39852         this.editor.toggleSourceEdit(sourceEditMode);
39853        
39854     },
39855      /**
39856      * Object collection of toolbar tooltips for the buttons in the editor. The key
39857      * is the command id associated with that button and the value is a valid QuickTips object.
39858      * For example:
39859 <pre><code>
39860 {
39861     bold : {
39862         title: 'Bold (Ctrl+B)',
39863         text: 'Make the selected text bold.',
39864         cls: 'x-html-editor-tip'
39865     },
39866     italic : {
39867         title: 'Italic (Ctrl+I)',
39868         text: 'Make the selected text italic.',
39869         cls: 'x-html-editor-tip'
39870     },
39871     ...
39872 </code></pre>
39873     * @type Object
39874      */
39875     buttonTips : {
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         underline : {
39887             title: 'Underline (Ctrl+U)',
39888             text: 'Underline the selected text.',
39889             cls: 'x-html-editor-tip'
39890         },
39891         increasefontsize : {
39892             title: 'Grow Text',
39893             text: 'Increase the font size.',
39894             cls: 'x-html-editor-tip'
39895         },
39896         decreasefontsize : {
39897             title: 'Shrink Text',
39898             text: 'Decrease the font size.',
39899             cls: 'x-html-editor-tip'
39900         },
39901         backcolor : {
39902             title: 'Text Highlight Color',
39903             text: 'Change the background color of the selected text.',
39904             cls: 'x-html-editor-tip'
39905         },
39906         forecolor : {
39907             title: 'Font Color',
39908             text: 'Change the color of the selected text.',
39909             cls: 'x-html-editor-tip'
39910         },
39911         justifyleft : {
39912             title: 'Align Text Left',
39913             text: 'Align text to the left.',
39914             cls: 'x-html-editor-tip'
39915         },
39916         justifycenter : {
39917             title: 'Center Text',
39918             text: 'Center text in the editor.',
39919             cls: 'x-html-editor-tip'
39920         },
39921         justifyright : {
39922             title: 'Align Text Right',
39923             text: 'Align text to the right.',
39924             cls: 'x-html-editor-tip'
39925         },
39926         insertunorderedlist : {
39927             title: 'Bullet List',
39928             text: 'Start a bulleted list.',
39929             cls: 'x-html-editor-tip'
39930         },
39931         insertorderedlist : {
39932             title: 'Numbered List',
39933             text: 'Start a numbered list.',
39934             cls: 'x-html-editor-tip'
39935         },
39936         createlink : {
39937             title: 'Hyperlink',
39938             text: 'Make the selected text a hyperlink.',
39939             cls: 'x-html-editor-tip'
39940         },
39941         sourceedit : {
39942             title: 'Source Edit',
39943             text: 'Switch to source editing mode.',
39944             cls: 'x-html-editor-tip'
39945         }
39946     },
39947     // private
39948     onDestroy : function(){
39949         if(this.rendered){
39950             
39951             this.tb.items.each(function(item){
39952                 if(item.menu){
39953                     item.menu.removeAll();
39954                     if(item.menu.el){
39955                         item.menu.el.destroy();
39956                     }
39957                 }
39958                 item.destroy();
39959             });
39960              
39961         }
39962     },
39963     onFirstFocus: function() {
39964         this.tb.items.each(function(item){
39965            item.enable();
39966         });
39967     }
39968 });
39969
39970
39971
39972
39973 // <script type="text/javascript">
39974 /*
39975  * Based on
39976  * Ext JS Library 1.1.1
39977  * Copyright(c) 2006-2007, Ext JS, LLC.
39978  *  
39979  
39980  */
39981
39982  
39983 /**
39984  * @class Roo.form.HtmlEditor.ToolbarContext
39985  * Context Toolbar
39986  * 
39987  * Usage:
39988  *
39989  new Roo.form.HtmlEditor({
39990     ....
39991     toolbars : [
39992         new Roo.form.HtmlEditor.ToolbarStandard(),
39993         new Roo.form.HtmlEditor.ToolbarContext()
39994         })
39995     }
39996      
39997  * 
39998  * @config : {Object} disable List of elements to disable.. (not done yet.)
39999  * 
40000  * 
40001  */
40002
40003 Roo.form.HtmlEditor.ToolbarContext = function(config)
40004 {
40005     
40006     Roo.apply(this, config);
40007     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40008     // dont call parent... till later.
40009 }
40010 Roo.form.HtmlEditor.ToolbarContext.types = {
40011     'IMG' : {
40012         width : {
40013             title: "Width",
40014             width: 40
40015         },
40016         height:  {
40017             title: "Height",
40018             width: 40
40019         },
40020         align: {
40021             title: "Align",
40022             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40023             width : 80
40024             
40025         },
40026         border: {
40027             title: "Border",
40028             width: 40
40029         },
40030         alt: {
40031             title: "Alt",
40032             width: 120
40033         },
40034         src : {
40035             title: "Src",
40036             width: 220
40037         }
40038         
40039     },
40040     'A' : {
40041         name : {
40042             title: "Name",
40043             width: 50
40044         },
40045         href:  {
40046             title: "Href",
40047             width: 220
40048         } // border?
40049         
40050     },
40051     'TABLE' : {
40052         rows : {
40053             title: "Rows",
40054             width: 20
40055         },
40056         cols : {
40057             title: "Cols",
40058             width: 20
40059         },
40060         width : {
40061             title: "Width",
40062             width: 40
40063         },
40064         height : {
40065             title: "Height",
40066             width: 40
40067         },
40068         border : {
40069             title: "Border",
40070             width: 20
40071         }
40072     },
40073     'TD' : {
40074         width : {
40075             title: "Width",
40076             width: 40
40077         },
40078         height : {
40079             title: "Height",
40080             width: 40
40081         },   
40082         align: {
40083             title: "Align",
40084             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40085             width: 40
40086         },
40087         valign: {
40088             title: "Valign",
40089             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40090             width: 40
40091         },
40092         colspan: {
40093             title: "Colspan",
40094             width: 20
40095             
40096         }
40097     },
40098     'INPUT' : {
40099         name : {
40100             title: "name",
40101             width: 120
40102         },
40103         value : {
40104             title: "Value",
40105             width: 120
40106         },
40107         width : {
40108             title: "Width",
40109             width: 40
40110         }
40111     },
40112     'LABEL' : {
40113         'for' : {
40114             title: "For",
40115             width: 120
40116         }
40117     },
40118     'TEXTAREA' : {
40119           name : {
40120             title: "name",
40121             width: 120
40122         },
40123         rows : {
40124             title: "Rows",
40125             width: 20
40126         },
40127         cols : {
40128             title: "Cols",
40129             width: 20
40130         }
40131     },
40132     'SELECT' : {
40133         name : {
40134             title: "name",
40135             width: 120
40136         },
40137         selectoptions : {
40138             title: "Options",
40139             width: 200
40140         }
40141     },
40142     'BODY' : {
40143         title : {
40144             title: "title",
40145             width: 120,
40146             disabled : true
40147         }
40148     }
40149 };
40150
40151
40152
40153 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40154     
40155     tb: false,
40156     
40157     rendered: false,
40158     
40159     editor : false,
40160     /**
40161      * @cfg {Object} disable  List of toolbar elements to disable
40162          
40163      */
40164     disable : false,
40165     
40166     
40167     
40168     toolbars : false,
40169     
40170     init : function(editor)
40171     {
40172         this.editor = editor;
40173         
40174         
40175         var fid = editor.frameId;
40176         var etb = this;
40177         function btn(id, toggle, handler){
40178             var xid = fid + '-'+ id ;
40179             return {
40180                 id : xid,
40181                 cmd : id,
40182                 cls : 'x-btn-icon x-edit-'+id,
40183                 enableToggle:toggle !== false,
40184                 scope: editor, // was editor...
40185                 handler:handler||editor.relayBtnCmd,
40186                 clickEvent:'mousedown',
40187                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40188                 tabIndex:-1
40189             };
40190         }
40191         // create a new element.
40192         var wdiv = editor.wrap.createChild({
40193                 tag: 'div'
40194             }, editor.wrap.dom.firstChild.nextSibling, true);
40195         
40196         // can we do this more than once??
40197         
40198          // stop form submits
40199       
40200  
40201         // disable everything...
40202         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40203         this.toolbars = {};
40204            
40205         for (var i in  ty) {
40206           
40207             this.toolbars[i] = this.buildToolbar(ty[i],i);
40208         }
40209         this.tb = this.toolbars.BODY;
40210         this.tb.el.show();
40211         
40212          
40213         this.rendered = true;
40214         
40215         // the all the btns;
40216         editor.on('editorevent', this.updateToolbar, this);
40217         // other toolbars need to implement this..
40218         //editor.on('editmodechange', this.updateToolbar, this);
40219     },
40220     
40221     
40222     
40223     /**
40224      * Protected method that will not generally be called directly. It triggers
40225      * a toolbar update by reading the markup state of the current selection in the editor.
40226      */
40227     updateToolbar: function(){
40228
40229         if(!this.editor.activated){
40230             this.editor.onFirstFocus();
40231             return;
40232         }
40233
40234         
40235         var ans = this.editor.getAllAncestors();
40236         
40237         // pick
40238         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40239         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40240         sel = sel ? sel : this.editor.doc.body;
40241         sel = sel.tagName.length ? sel : this.editor.doc.body;
40242         var tn = sel.tagName.toUpperCase();
40243         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40244         tn = sel.tagName.toUpperCase();
40245         if (this.tb.name  == tn) {
40246             return; // no change
40247         }
40248         this.tb.el.hide();
40249         ///console.log("show: " + tn);
40250         this.tb =  this.toolbars[tn];
40251         this.tb.el.show();
40252         this.tb.fields.each(function(e) {
40253             e.setValue(sel.getAttribute(e.name));
40254         });
40255         this.tb.selectedNode = sel;
40256         
40257         
40258         Roo.menu.MenuMgr.hideAll();
40259
40260         //this.editorsyncValue();
40261     },
40262    
40263        
40264     // private
40265     onDestroy : function(){
40266         if(this.rendered){
40267             
40268             this.tb.items.each(function(item){
40269                 if(item.menu){
40270                     item.menu.removeAll();
40271                     if(item.menu.el){
40272                         item.menu.el.destroy();
40273                     }
40274                 }
40275                 item.destroy();
40276             });
40277              
40278         }
40279     },
40280     onFirstFocus: function() {
40281         // need to do this for all the toolbars..
40282         this.tb.items.each(function(item){
40283            item.enable();
40284         });
40285     },
40286     buildToolbar: function(tlist, nm)
40287     {
40288         var editor = this.editor;
40289          // create a new element.
40290         var wdiv = editor.wrap.createChild({
40291                 tag: 'div'
40292             }, editor.wrap.dom.firstChild.nextSibling, true);
40293         
40294        
40295         var tb = new Roo.Toolbar(wdiv);
40296         tb.add(nm+ ":&nbsp;");
40297         for (var i in tlist) {
40298             var item = tlist[i];
40299             tb.add(item.title + ":&nbsp;");
40300             if (item.opts) {
40301                 // fixme
40302                 
40303               
40304                 tb.addField( new Roo.form.ComboBox({
40305                     store: new Roo.data.SimpleStore({
40306                         id : 'val',
40307                         fields: ['val'],
40308                         data : item.opts // from states.js
40309                     }),
40310                     name : i,
40311                     displayField:'val',
40312                     typeAhead: false,
40313                     mode: 'local',
40314                     editable : false,
40315                     triggerAction: 'all',
40316                     emptyText:'Select',
40317                     selectOnFocus:true,
40318                     width: item.width ? item.width  : 130,
40319                     listeners : {
40320                         'select': function(c, r, i) {
40321                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40322                         }
40323                     }
40324
40325                 }));
40326                 continue;
40327                     
40328                 
40329                 
40330                 
40331                 
40332                 tb.addField( new Roo.form.TextField({
40333                     name: i,
40334                     width: 100,
40335                     //allowBlank:false,
40336                     value: ''
40337                 }));
40338                 continue;
40339             }
40340             tb.addField( new Roo.form.TextField({
40341                 name: i,
40342                 width: item.width,
40343                 //allowBlank:true,
40344                 value: '',
40345                 listeners: {
40346                     'change' : function(f, nv, ov) {
40347                         tb.selectedNode.setAttribute(f.name, nv);
40348                     }
40349                 }
40350             }));
40351              
40352         }
40353         tb.el.on('click', function(e){
40354             e.preventDefault(); // what does this do?
40355         });
40356         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40357         tb.el.hide();
40358         tb.name = nm;
40359         // dont need to disable them... as they will get hidden
40360         return tb;
40361          
40362         
40363     }
40364     
40365     
40366     
40367     
40368 });
40369
40370
40371
40372
40373
40374 /*
40375  * Based on:
40376  * Ext JS Library 1.1.1
40377  * Copyright(c) 2006-2007, Ext JS, LLC.
40378  *
40379  * Originally Released Under LGPL - original licence link has changed is not relivant.
40380  *
40381  * Fork - LGPL
40382  * <script type="text/javascript">
40383  */
40384  
40385 /**
40386  * @class Roo.form.BasicForm
40387  * @extends Roo.util.Observable
40388  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40389  * @constructor
40390  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40391  * @param {Object} config Configuration options
40392  */
40393 Roo.form.BasicForm = function(el, config){
40394     this.allItems = [];
40395     this.childForms = [];
40396     Roo.apply(this, config);
40397     /*
40398      * The Roo.form.Field items in this form.
40399      * @type MixedCollection
40400      */
40401      
40402      
40403     this.items = new Roo.util.MixedCollection(false, function(o){
40404         return o.id || (o.id = Roo.id());
40405     });
40406     this.addEvents({
40407         /**
40408          * @event beforeaction
40409          * Fires before any action is performed. Return false to cancel the action.
40410          * @param {Form} this
40411          * @param {Action} action The action to be performed
40412          */
40413         beforeaction: true,
40414         /**
40415          * @event actionfailed
40416          * Fires when an action fails.
40417          * @param {Form} this
40418          * @param {Action} action The action that failed
40419          */
40420         actionfailed : true,
40421         /**
40422          * @event actioncomplete
40423          * Fires when an action is completed.
40424          * @param {Form} this
40425          * @param {Action} action The action that completed
40426          */
40427         actioncomplete : true
40428     });
40429     if(el){
40430         this.initEl(el);
40431     }
40432     Roo.form.BasicForm.superclass.constructor.call(this);
40433 };
40434
40435 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40436     /**
40437      * @cfg {String} method
40438      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40439      */
40440     /**
40441      * @cfg {DataReader} reader
40442      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40443      * This is optional as there is built-in support for processing JSON.
40444      */
40445     /**
40446      * @cfg {DataReader} errorReader
40447      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40448      * This is completely optional as there is built-in support for processing JSON.
40449      */
40450     /**
40451      * @cfg {String} url
40452      * The URL to use for form actions if one isn't supplied in the action options.
40453      */
40454     /**
40455      * @cfg {Boolean} fileUpload
40456      * Set to true if this form is a file upload.
40457      */
40458      
40459     /**
40460      * @cfg {Object} baseParams
40461      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40462      */
40463      /**
40464      
40465     /**
40466      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40467      */
40468     timeout: 30,
40469
40470     // private
40471     activeAction : null,
40472
40473     /**
40474      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40475      * or setValues() data instead of when the form was first created.
40476      */
40477     trackResetOnLoad : false,
40478     
40479     
40480     /**
40481      * childForms - used for multi-tab forms
40482      * @type {Array}
40483      */
40484     childForms : false,
40485     
40486     /**
40487      * allItems - full list of fields.
40488      * @type {Array}
40489      */
40490     allItems : false,
40491     
40492     /**
40493      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40494      * element by passing it or its id or mask the form itself by passing in true.
40495      * @type Mixed
40496      */
40497     waitMsgTarget : false,
40498
40499     // private
40500     initEl : function(el){
40501         this.el = Roo.get(el);
40502         this.id = this.el.id || Roo.id();
40503         this.el.on('submit', this.onSubmit, this);
40504         this.el.addClass('x-form');
40505     },
40506
40507     // private
40508     onSubmit : function(e){
40509         e.stopEvent();
40510     },
40511
40512     /**
40513      * Returns true if client-side validation on the form is successful.
40514      * @return Boolean
40515      */
40516     isValid : function(){
40517         var valid = true;
40518         this.items.each(function(f){
40519            if(!f.validate()){
40520                valid = false;
40521            }
40522         });
40523         return valid;
40524     },
40525
40526     /**
40527      * Returns true if any fields in this form have changed since their original load.
40528      * @return Boolean
40529      */
40530     isDirty : function(){
40531         var dirty = false;
40532         this.items.each(function(f){
40533            if(f.isDirty()){
40534                dirty = true;
40535                return false;
40536            }
40537         });
40538         return dirty;
40539     },
40540
40541     /**
40542      * Performs a predefined action (submit or load) or custom actions you define on this form.
40543      * @param {String} actionName The name of the action type
40544      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40545      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40546      * accept other config options):
40547      * <pre>
40548 Property          Type             Description
40549 ----------------  ---------------  ----------------------------------------------------------------------------------
40550 url               String           The url for the action (defaults to the form's url)
40551 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40552 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40553 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40554                                    validate the form on the client (defaults to false)
40555      * </pre>
40556      * @return {BasicForm} this
40557      */
40558     doAction : function(action, options){
40559         if(typeof action == 'string'){
40560             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40561         }
40562         if(this.fireEvent('beforeaction', this, action) !== false){
40563             this.beforeAction(action);
40564             action.run.defer(100, action);
40565         }
40566         return this;
40567     },
40568
40569     /**
40570      * Shortcut to do a submit action.
40571      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40572      * @return {BasicForm} this
40573      */
40574     submit : function(options){
40575         this.doAction('submit', options);
40576         return this;
40577     },
40578
40579     /**
40580      * Shortcut to do a load action.
40581      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40582      * @return {BasicForm} this
40583      */
40584     load : function(options){
40585         this.doAction('load', options);
40586         return this;
40587     },
40588
40589     /**
40590      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40591      * @param {Record} record The record to edit
40592      * @return {BasicForm} this
40593      */
40594     updateRecord : function(record){
40595         record.beginEdit();
40596         var fs = record.fields;
40597         fs.each(function(f){
40598             var field = this.findField(f.name);
40599             if(field){
40600                 record.set(f.name, field.getValue());
40601             }
40602         }, this);
40603         record.endEdit();
40604         return this;
40605     },
40606
40607     /**
40608      * Loads an Roo.data.Record into this form.
40609      * @param {Record} record The record to load
40610      * @return {BasicForm} this
40611      */
40612     loadRecord : function(record){
40613         this.setValues(record.data);
40614         return this;
40615     },
40616
40617     // private
40618     beforeAction : function(action){
40619         var o = action.options;
40620         
40621        
40622         if(this.waitMsgTarget === true){
40623             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40624         }else if(this.waitMsgTarget){
40625             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40626             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40627         }else {
40628             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40629         }
40630          
40631     },
40632
40633     // private
40634     afterAction : function(action, success){
40635         this.activeAction = null;
40636         var o = action.options;
40637         
40638         if(this.waitMsgTarget === true){
40639             this.el.unmask();
40640         }else if(this.waitMsgTarget){
40641             this.waitMsgTarget.unmask();
40642         }else{
40643             Roo.MessageBox.updateProgress(1);
40644             Roo.MessageBox.hide();
40645         }
40646          
40647         if(success){
40648             if(o.reset){
40649                 this.reset();
40650             }
40651             Roo.callback(o.success, o.scope, [this, action]);
40652             this.fireEvent('actioncomplete', this, action);
40653             
40654         }else{
40655             Roo.callback(o.failure, o.scope, [this, action]);
40656             // show an error message if no failed handler is set..
40657             if (!this.hasListener('actionfailed')) {
40658                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40659             }
40660             
40661             this.fireEvent('actionfailed', this, action);
40662         }
40663         
40664     },
40665
40666     /**
40667      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40668      * @param {String} id The value to search for
40669      * @return Field
40670      */
40671     findField : function(id){
40672         var field = this.items.get(id);
40673         if(!field){
40674             this.items.each(function(f){
40675                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40676                     field = f;
40677                     return false;
40678                 }
40679             });
40680         }
40681         return field || null;
40682     },
40683
40684     /**
40685      * Add a secondary form to this one, 
40686      * Used to provide tabbed forms. One form is primary, with hidden values 
40687      * which mirror the elements from the other forms.
40688      * 
40689      * @param {Roo.form.Form} form to add.
40690      * 
40691      */
40692     addForm : function(form)
40693     {
40694        
40695         if (this.childForms.indexOf(form) > -1) {
40696             // already added..
40697             return;
40698         }
40699         this.childForms.push(form);
40700         var n = '';
40701         Roo.each(form.allItems, function (fe) {
40702             
40703             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40704             if (this.findField(n)) { // already added..
40705                 return;
40706             }
40707             var add = new Roo.form.Hidden({
40708                 name : n
40709             });
40710             add.render(this.el);
40711             
40712             this.add( add );
40713         }, this);
40714         
40715     },
40716     /**
40717      * Mark fields in this form invalid in bulk.
40718      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40719      * @return {BasicForm} this
40720      */
40721     markInvalid : function(errors){
40722         if(errors instanceof Array){
40723             for(var i = 0, len = errors.length; i < len; i++){
40724                 var fieldError = errors[i];
40725                 var f = this.findField(fieldError.id);
40726                 if(f){
40727                     f.markInvalid(fieldError.msg);
40728                 }
40729             }
40730         }else{
40731             var field, id;
40732             for(id in errors){
40733                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40734                     field.markInvalid(errors[id]);
40735                 }
40736             }
40737         }
40738         Roo.each(this.childForms || [], function (f) {
40739             f.markInvalid(errors);
40740         });
40741         
40742         return this;
40743     },
40744
40745     /**
40746      * Set values for fields in this form in bulk.
40747      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40748      * @return {BasicForm} this
40749      */
40750     setValues : function(values){
40751         if(values instanceof Array){ // array of objects
40752             for(var i = 0, len = values.length; i < len; i++){
40753                 var v = values[i];
40754                 var f = this.findField(v.id);
40755                 if(f){
40756                     f.setValue(v.value);
40757                     if(this.trackResetOnLoad){
40758                         f.originalValue = f.getValue();
40759                     }
40760                 }
40761             }
40762         }else{ // object hash
40763             var field, id;
40764             for(id in values){
40765                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40766                     
40767                     if (field.setFromData && 
40768                         field.valueField && 
40769                         field.displayField &&
40770                         // combos' with local stores can 
40771                         // be queried via setValue()
40772                         // to set their value..
40773                         (field.store && !field.store.isLocal)
40774                         ) {
40775                         // it's a combo
40776                         var sd = { };
40777                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40778                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40779                         field.setFromData(sd);
40780                         
40781                     } else {
40782                         field.setValue(values[id]);
40783                     }
40784                     
40785                     
40786                     if(this.trackResetOnLoad){
40787                         field.originalValue = field.getValue();
40788                     }
40789                 }
40790             }
40791         }
40792          
40793         Roo.each(this.childForms || [], function (f) {
40794             f.setValues(values);
40795         });
40796                 
40797         return this;
40798     },
40799
40800     /**
40801      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40802      * they are returned as an array.
40803      * @param {Boolean} asString
40804      * @return {Object}
40805      */
40806     getValues : function(asString){
40807         if (this.childForms) {
40808             // copy values from the child forms
40809             Roo.each(this.childForms, function (f) {
40810                 this.setValues(f.getValues());
40811             }, this);
40812         }
40813         
40814         
40815         
40816         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40817         if(asString === true){
40818             return fs;
40819         }
40820         return Roo.urlDecode(fs);
40821     },
40822     
40823     /**
40824      * Returns the fields in this form as an object with key/value pairs. 
40825      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40826      * @return {Object}
40827      */
40828     getFieldValues : function()
40829     {
40830         if (this.childForms) {
40831             // copy values from the child forms
40832             Roo.each(this.childForms, function (f) {
40833                 this.setValues(f.getValues());
40834             }, this);
40835         }
40836         
40837         var ret = {};
40838         this.items.each(function(f){
40839             if (!f.getName()) {
40840                 return;
40841             }
40842             var v = f.getValue();
40843             if ((typeof(v) == 'object') && f.getRawValue) {
40844                 v = f.getRawValue() ; // dates..
40845             }
40846             ret[f.getName()] = v;
40847         });
40848         
40849         return ret;
40850     },
40851
40852     /**
40853      * Clears all invalid messages in this form.
40854      * @return {BasicForm} this
40855      */
40856     clearInvalid : function(){
40857         this.items.each(function(f){
40858            f.clearInvalid();
40859         });
40860         
40861         Roo.each(this.childForms || [], function (f) {
40862             f.clearInvalid();
40863         });
40864         
40865         
40866         return this;
40867     },
40868
40869     /**
40870      * Resets this form.
40871      * @return {BasicForm} this
40872      */
40873     reset : function(){
40874         this.items.each(function(f){
40875             f.reset();
40876         });
40877         
40878         Roo.each(this.childForms || [], function (f) {
40879             f.reset();
40880         });
40881        
40882         
40883         return this;
40884     },
40885
40886     /**
40887      * Add Roo.form components to this form.
40888      * @param {Field} field1
40889      * @param {Field} field2 (optional)
40890      * @param {Field} etc (optional)
40891      * @return {BasicForm} this
40892      */
40893     add : function(){
40894         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40895         return this;
40896     },
40897
40898
40899     /**
40900      * Removes a field from the items collection (does NOT remove its markup).
40901      * @param {Field} field
40902      * @return {BasicForm} this
40903      */
40904     remove : function(field){
40905         this.items.remove(field);
40906         return this;
40907     },
40908
40909     /**
40910      * Looks at the fields in this form, checks them for an id attribute,
40911      * and calls applyTo on the existing dom element with that id.
40912      * @return {BasicForm} this
40913      */
40914     render : function(){
40915         this.items.each(function(f){
40916             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40917                 f.applyTo(f.id);
40918             }
40919         });
40920         return this;
40921     },
40922
40923     /**
40924      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40925      * @param {Object} values
40926      * @return {BasicForm} this
40927      */
40928     applyToFields : function(o){
40929         this.items.each(function(f){
40930            Roo.apply(f, o);
40931         });
40932         return this;
40933     },
40934
40935     /**
40936      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40937      * @param {Object} values
40938      * @return {BasicForm} this
40939      */
40940     applyIfToFields : function(o){
40941         this.items.each(function(f){
40942            Roo.applyIf(f, o);
40943         });
40944         return this;
40945     }
40946 });
40947
40948 // back compat
40949 Roo.BasicForm = Roo.form.BasicForm;/*
40950  * Based on:
40951  * Ext JS Library 1.1.1
40952  * Copyright(c) 2006-2007, Ext JS, LLC.
40953  *
40954  * Originally Released Under LGPL - original licence link has changed is not relivant.
40955  *
40956  * Fork - LGPL
40957  * <script type="text/javascript">
40958  */
40959
40960 /**
40961  * @class Roo.form.Form
40962  * @extends Roo.form.BasicForm
40963  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40964  * @constructor
40965  * @param {Object} config Configuration options
40966  */
40967 Roo.form.Form = function(config){
40968     var xitems =  [];
40969     if (config.items) {
40970         xitems = config.items;
40971         delete config.items;
40972     }
40973    
40974     
40975     Roo.form.Form.superclass.constructor.call(this, null, config);
40976     this.url = this.url || this.action;
40977     if(!this.root){
40978         this.root = new Roo.form.Layout(Roo.applyIf({
40979             id: Roo.id()
40980         }, config));
40981     }
40982     this.active = this.root;
40983     /**
40984      * Array of all the buttons that have been added to this form via {@link addButton}
40985      * @type Array
40986      */
40987     this.buttons = [];
40988     this.allItems = [];
40989     this.addEvents({
40990         /**
40991          * @event clientvalidation
40992          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40993          * @param {Form} this
40994          * @param {Boolean} valid true if the form has passed client-side validation
40995          */
40996         clientvalidation: true,
40997         /**
40998          * @event rendered
40999          * Fires when the form is rendered
41000          * @param {Roo.form.Form} form
41001          */
41002         rendered : true
41003     });
41004     
41005     if (this.progressUrl) {
41006             // push a hidden field onto the list of fields..
41007             this.addxtype( {
41008                     xns: Roo.form, 
41009                     xtype : 'Hidden', 
41010                     name : 'UPLOAD_IDENTIFIER' 
41011             });
41012         }
41013         
41014     
41015     Roo.each(xitems, this.addxtype, this);
41016     
41017     
41018     
41019 };
41020
41021 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41022     /**
41023      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41024      */
41025     /**
41026      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41027      */
41028     /**
41029      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41030      */
41031     buttonAlign:'center',
41032
41033     /**
41034      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41035      */
41036     minButtonWidth:75,
41037
41038     /**
41039      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41040      * This property cascades to child containers if not set.
41041      */
41042     labelAlign:'left',
41043
41044     /**
41045      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41046      * fires a looping event with that state. This is required to bind buttons to the valid
41047      * state using the config value formBind:true on the button.
41048      */
41049     monitorValid : false,
41050
41051     /**
41052      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41053      */
41054     monitorPoll : 200,
41055     
41056     /**
41057      * @cfg {String} progressUrl - Url to return progress data 
41058      */
41059     
41060     progressUrl : false,
41061   
41062     /**
41063      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41064      * fields are added and the column is closed. If no fields are passed the column remains open
41065      * until end() is called.
41066      * @param {Object} config The config to pass to the column
41067      * @param {Field} field1 (optional)
41068      * @param {Field} field2 (optional)
41069      * @param {Field} etc (optional)
41070      * @return Column The column container object
41071      */
41072     column : function(c){
41073         var col = new Roo.form.Column(c);
41074         this.start(col);
41075         if(arguments.length > 1){ // duplicate code required because of Opera
41076             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41077             this.end();
41078         }
41079         return col;
41080     },
41081
41082     /**
41083      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41084      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41085      * until end() is called.
41086      * @param {Object} config The config to pass to the fieldset
41087      * @param {Field} field1 (optional)
41088      * @param {Field} field2 (optional)
41089      * @param {Field} etc (optional)
41090      * @return FieldSet The fieldset container object
41091      */
41092     fieldset : function(c){
41093         var fs = new Roo.form.FieldSet(c);
41094         this.start(fs);
41095         if(arguments.length > 1){ // duplicate code required because of Opera
41096             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41097             this.end();
41098         }
41099         return fs;
41100     },
41101
41102     /**
41103      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41104      * fields are added and the container is closed. If no fields are passed the container remains open
41105      * until end() is called.
41106      * @param {Object} config The config to pass to the Layout
41107      * @param {Field} field1 (optional)
41108      * @param {Field} field2 (optional)
41109      * @param {Field} etc (optional)
41110      * @return Layout The container object
41111      */
41112     container : function(c){
41113         var l = new Roo.form.Layout(c);
41114         this.start(l);
41115         if(arguments.length > 1){ // duplicate code required because of Opera
41116             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41117             this.end();
41118         }
41119         return l;
41120     },
41121
41122     /**
41123      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41124      * @param {Object} container A Roo.form.Layout or subclass of Layout
41125      * @return {Form} this
41126      */
41127     start : function(c){
41128         // cascade label info
41129         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41130         this.active.stack.push(c);
41131         c.ownerCt = this.active;
41132         this.active = c;
41133         return this;
41134     },
41135
41136     /**
41137      * Closes the current open container
41138      * @return {Form} this
41139      */
41140     end : function(){
41141         if(this.active == this.root){
41142             return this;
41143         }
41144         this.active = this.active.ownerCt;
41145         return this;
41146     },
41147
41148     /**
41149      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41150      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41151      * as the label of the field.
41152      * @param {Field} field1
41153      * @param {Field} field2 (optional)
41154      * @param {Field} etc. (optional)
41155      * @return {Form} this
41156      */
41157     add : function(){
41158         this.active.stack.push.apply(this.active.stack, arguments);
41159         this.allItems.push.apply(this.allItems,arguments);
41160         var r = [];
41161         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41162             if(a[i].isFormField){
41163                 r.push(a[i]);
41164             }
41165         }
41166         if(r.length > 0){
41167             Roo.form.Form.superclass.add.apply(this, r);
41168         }
41169         return this;
41170     },
41171     
41172
41173     
41174     
41175     
41176      /**
41177      * Find any element that has been added to a form, using it's ID or name
41178      * This can include framesets, columns etc. along with regular fields..
41179      * @param {String} id - id or name to find.
41180      
41181      * @return {Element} e - or false if nothing found.
41182      */
41183     findbyId : function(id)
41184     {
41185         var ret = false;
41186         if (!id) {
41187             return ret;
41188         }
41189         Roo.each(this.allItems, function(f){
41190             if (f.id == id || f.name == id ){
41191                 ret = f;
41192                 return false;
41193             }
41194         });
41195         return ret;
41196     },
41197
41198     
41199     
41200     /**
41201      * Render this form into the passed container. This should only be called once!
41202      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41203      * @return {Form} this
41204      */
41205     render : function(ct)
41206     {
41207         
41208         
41209         
41210         ct = Roo.get(ct);
41211         var o = this.autoCreate || {
41212             tag: 'form',
41213             method : this.method || 'POST',
41214             id : this.id || Roo.id()
41215         };
41216         this.initEl(ct.createChild(o));
41217
41218         this.root.render(this.el);
41219         
41220        
41221              
41222         this.items.each(function(f){
41223             f.render('x-form-el-'+f.id);
41224         });
41225
41226         if(this.buttons.length > 0){
41227             // tables are required to maintain order and for correct IE layout
41228             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41229                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41230                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41231             }}, null, true);
41232             var tr = tb.getElementsByTagName('tr')[0];
41233             for(var i = 0, len = this.buttons.length; i < len; i++) {
41234                 var b = this.buttons[i];
41235                 var td = document.createElement('td');
41236                 td.className = 'x-form-btn-td';
41237                 b.render(tr.appendChild(td));
41238             }
41239         }
41240         if(this.monitorValid){ // initialize after render
41241             this.startMonitoring();
41242         }
41243         this.fireEvent('rendered', this);
41244         return this;
41245     },
41246
41247     /**
41248      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41249      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41250      * object or a valid Roo.DomHelper element config
41251      * @param {Function} handler The function called when the button is clicked
41252      * @param {Object} scope (optional) The scope of the handler function
41253      * @return {Roo.Button}
41254      */
41255     addButton : function(config, handler, scope){
41256         var bc = {
41257             handler: handler,
41258             scope: scope,
41259             minWidth: this.minButtonWidth,
41260             hideParent:true
41261         };
41262         if(typeof config == "string"){
41263             bc.text = config;
41264         }else{
41265             Roo.apply(bc, config);
41266         }
41267         var btn = new Roo.Button(null, bc);
41268         this.buttons.push(btn);
41269         return btn;
41270     },
41271
41272      /**
41273      * Adds a series of form elements (using the xtype property as the factory method.
41274      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41275      * @param {Object} config 
41276      */
41277     
41278     addxtype : function()
41279     {
41280         var ar = Array.prototype.slice.call(arguments, 0);
41281         var ret = false;
41282         for(var i = 0; i < ar.length; i++) {
41283             if (!ar[i]) {
41284                 continue; // skip -- if this happends something invalid got sent, we 
41285                 // should ignore it, as basically that interface element will not show up
41286                 // and that should be pretty obvious!!
41287             }
41288             
41289             if (Roo.form[ar[i].xtype]) {
41290                 ar[i].form = this;
41291                 var fe = Roo.factory(ar[i], Roo.form);
41292                 if (!ret) {
41293                     ret = fe;
41294                 }
41295                 fe.form = this;
41296                 if (fe.store) {
41297                     fe.store.form = this;
41298                 }
41299                 if (fe.isLayout) {  
41300                          
41301                     this.start(fe);
41302                     this.allItems.push(fe);
41303                     if (fe.items && fe.addxtype) {
41304                         fe.addxtype.apply(fe, fe.items);
41305                         delete fe.items;
41306                     }
41307                      this.end();
41308                     continue;
41309                 }
41310                 
41311                 
41312                  
41313                 this.add(fe);
41314               //  console.log('adding ' + ar[i].xtype);
41315             }
41316             if (ar[i].xtype == 'Button') {  
41317                 //console.log('adding button');
41318                 //console.log(ar[i]);
41319                 this.addButton(ar[i]);
41320                 this.allItems.push(fe);
41321                 continue;
41322             }
41323             
41324             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41325                 alert('end is not supported on xtype any more, use items');
41326             //    this.end();
41327             //    //console.log('adding end');
41328             }
41329             
41330         }
41331         return ret;
41332     },
41333     
41334     /**
41335      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41336      * option "monitorValid"
41337      */
41338     startMonitoring : function(){
41339         if(!this.bound){
41340             this.bound = true;
41341             Roo.TaskMgr.start({
41342                 run : this.bindHandler,
41343                 interval : this.monitorPoll || 200,
41344                 scope: this
41345             });
41346         }
41347     },
41348
41349     /**
41350      * Stops monitoring of the valid state of this form
41351      */
41352     stopMonitoring : function(){
41353         this.bound = false;
41354     },
41355
41356     // private
41357     bindHandler : function(){
41358         if(!this.bound){
41359             return false; // stops binding
41360         }
41361         var valid = true;
41362         this.items.each(function(f){
41363             if(!f.isValid(true)){
41364                 valid = false;
41365                 return false;
41366             }
41367         });
41368         for(var i = 0, len = this.buttons.length; i < len; i++){
41369             var btn = this.buttons[i];
41370             if(btn.formBind === true && btn.disabled === valid){
41371                 btn.setDisabled(!valid);
41372             }
41373         }
41374         this.fireEvent('clientvalidation', this, valid);
41375     }
41376     
41377     
41378     
41379     
41380     
41381     
41382     
41383     
41384 });
41385
41386
41387 // back compat
41388 Roo.Form = Roo.form.Form;
41389 /*
41390  * Based on:
41391  * Ext JS Library 1.1.1
41392  * Copyright(c) 2006-2007, Ext JS, LLC.
41393  *
41394  * Originally Released Under LGPL - original licence link has changed is not relivant.
41395  *
41396  * Fork - LGPL
41397  * <script type="text/javascript">
41398  */
41399  
41400  /**
41401  * @class Roo.form.Action
41402  * Internal Class used to handle form actions
41403  * @constructor
41404  * @param {Roo.form.BasicForm} el The form element or its id
41405  * @param {Object} config Configuration options
41406  */
41407  
41408  
41409 // define the action interface
41410 Roo.form.Action = function(form, options){
41411     this.form = form;
41412     this.options = options || {};
41413 };
41414 /**
41415  * Client Validation Failed
41416  * @const 
41417  */
41418 Roo.form.Action.CLIENT_INVALID = 'client';
41419 /**
41420  * Server Validation Failed
41421  * @const 
41422  */
41423  Roo.form.Action.SERVER_INVALID = 'server';
41424  /**
41425  * Connect to Server Failed
41426  * @const 
41427  */
41428 Roo.form.Action.CONNECT_FAILURE = 'connect';
41429 /**
41430  * Reading Data from Server Failed
41431  * @const 
41432  */
41433 Roo.form.Action.LOAD_FAILURE = 'load';
41434
41435 Roo.form.Action.prototype = {
41436     type : 'default',
41437     failureType : undefined,
41438     response : undefined,
41439     result : undefined,
41440
41441     // interface method
41442     run : function(options){
41443
41444     },
41445
41446     // interface method
41447     success : function(response){
41448
41449     },
41450
41451     // interface method
41452     handleResponse : function(response){
41453
41454     },
41455
41456     // default connection failure
41457     failure : function(response){
41458         
41459         this.response = response;
41460         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41461         this.form.afterAction(this, false);
41462     },
41463
41464     processResponse : function(response){
41465         this.response = response;
41466         if(!response.responseText){
41467             return true;
41468         }
41469         this.result = this.handleResponse(response);
41470         return this.result;
41471     },
41472
41473     // utility functions used internally
41474     getUrl : function(appendParams){
41475         var url = this.options.url || this.form.url || this.form.el.dom.action;
41476         if(appendParams){
41477             var p = this.getParams();
41478             if(p){
41479                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41480             }
41481         }
41482         return url;
41483     },
41484
41485     getMethod : function(){
41486         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41487     },
41488
41489     getParams : function(){
41490         var bp = this.form.baseParams;
41491         var p = this.options.params;
41492         if(p){
41493             if(typeof p == "object"){
41494                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41495             }else if(typeof p == 'string' && bp){
41496                 p += '&' + Roo.urlEncode(bp);
41497             }
41498         }else if(bp){
41499             p = Roo.urlEncode(bp);
41500         }
41501         return p;
41502     },
41503
41504     createCallback : function(){
41505         return {
41506             success: this.success,
41507             failure: this.failure,
41508             scope: this,
41509             timeout: (this.form.timeout*1000),
41510             upload: this.form.fileUpload ? this.success : undefined
41511         };
41512     }
41513 };
41514
41515 Roo.form.Action.Submit = function(form, options){
41516     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41517 };
41518
41519 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41520     type : 'submit',
41521
41522     haveProgress : false,
41523     uploadComplete : false,
41524     
41525     // uploadProgress indicator.
41526     uploadProgress : function()
41527     {
41528         if (!this.form.progressUrl) {
41529             return;
41530         }
41531         
41532         if (!this.haveProgress) {
41533             Roo.MessageBox.progress("Uploading", "Uploading");
41534         }
41535         if (this.uploadComplete) {
41536            Roo.MessageBox.hide();
41537            return;
41538         }
41539         
41540         this.haveProgress = true;
41541    
41542         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41543         
41544         var c = new Roo.data.Connection();
41545         c.request({
41546             url : this.form.progressUrl,
41547             params: {
41548                 id : uid
41549             },
41550             method: 'GET',
41551             success : function(req){
41552                //console.log(data);
41553                 var rdata = false;
41554                 var edata;
41555                 try  {
41556                    rdata = Roo.decode(req.responseText)
41557                 } catch (e) {
41558                     Roo.log("Invalid data from server..");
41559                     Roo.log(edata);
41560                     return;
41561                 }
41562                 if (!rdata || !rdata.success) {
41563                     Roo.log(rdata);
41564                     return;
41565                 }
41566                 var data = rdata.data;
41567                 
41568                 if (this.uploadComplete) {
41569                    Roo.MessageBox.hide();
41570                    return;
41571                 }
41572                    
41573                 if (data){
41574                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41575                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41576                     );
41577                 }
41578                 this.uploadProgress.defer(2000,this);
41579             },
41580        
41581             failure: function(data) {
41582                 Roo.log('progress url failed ');
41583                 Roo.log(data);
41584             },
41585             scope : this
41586         });
41587            
41588     },
41589     
41590     
41591     run : function()
41592     {
41593         // run get Values on the form, so it syncs any secondary forms.
41594         this.form.getValues();
41595         
41596         var o = this.options;
41597         var method = this.getMethod();
41598         var isPost = method == 'POST';
41599         if(o.clientValidation === false || this.form.isValid()){
41600             
41601             if (this.form.progressUrl) {
41602                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41603                     (new Date() * 1) + '' + Math.random());
41604                     
41605             } 
41606             
41607             
41608             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41609                 form:this.form.el.dom,
41610                 url:this.getUrl(!isPost),
41611                 method: method,
41612                 params:isPost ? this.getParams() : null,
41613                 isUpload: this.form.fileUpload
41614             }));
41615             
41616             this.uploadProgress();
41617
41618         }else if (o.clientValidation !== false){ // client validation failed
41619             this.failureType = Roo.form.Action.CLIENT_INVALID;
41620             this.form.afterAction(this, false);
41621         }
41622     },
41623
41624     success : function(response)
41625     {
41626         this.uploadComplete= true;
41627         if (this.haveProgress) {
41628             Roo.MessageBox.hide();
41629         }
41630         
41631         
41632         var result = this.processResponse(response);
41633         if(result === true || result.success){
41634             this.form.afterAction(this, true);
41635             return;
41636         }
41637         if(result.errors){
41638             this.form.markInvalid(result.errors);
41639             this.failureType = Roo.form.Action.SERVER_INVALID;
41640         }
41641         this.form.afterAction(this, false);
41642     },
41643     failure : function(response)
41644     {
41645         this.uploadComplete= true;
41646         if (this.haveProgress) {
41647             Roo.MessageBox.hide();
41648         }
41649         
41650         
41651         this.response = response;
41652         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41653         this.form.afterAction(this, false);
41654     },
41655     
41656     handleResponse : function(response){
41657         if(this.form.errorReader){
41658             var rs = this.form.errorReader.read(response);
41659             var errors = [];
41660             if(rs.records){
41661                 for(var i = 0, len = rs.records.length; i < len; i++) {
41662                     var r = rs.records[i];
41663                     errors[i] = r.data;
41664                 }
41665             }
41666             if(errors.length < 1){
41667                 errors = null;
41668             }
41669             return {
41670                 success : rs.success,
41671                 errors : errors
41672             };
41673         }
41674         var ret = false;
41675         try {
41676             ret = Roo.decode(response.responseText);
41677         } catch (e) {
41678             ret = {
41679                 success: false,
41680                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41681                 errors : []
41682             };
41683         }
41684         return ret;
41685         
41686     }
41687 });
41688
41689
41690 Roo.form.Action.Load = function(form, options){
41691     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41692     this.reader = this.form.reader;
41693 };
41694
41695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41696     type : 'load',
41697
41698     run : function(){
41699         
41700         Roo.Ajax.request(Roo.apply(
41701                 this.createCallback(), {
41702                     method:this.getMethod(),
41703                     url:this.getUrl(false),
41704                     params:this.getParams()
41705         }));
41706     },
41707
41708     success : function(response){
41709         
41710         var result = this.processResponse(response);
41711         if(result === true || !result.success || !result.data){
41712             this.failureType = Roo.form.Action.LOAD_FAILURE;
41713             this.form.afterAction(this, false);
41714             return;
41715         }
41716         this.form.clearInvalid();
41717         this.form.setValues(result.data);
41718         this.form.afterAction(this, true);
41719     },
41720
41721     handleResponse : function(response){
41722         if(this.form.reader){
41723             var rs = this.form.reader.read(response);
41724             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41725             return {
41726                 success : rs.success,
41727                 data : data
41728             };
41729         }
41730         return Roo.decode(response.responseText);
41731     }
41732 });
41733
41734 Roo.form.Action.ACTION_TYPES = {
41735     'load' : Roo.form.Action.Load,
41736     'submit' : Roo.form.Action.Submit
41737 };/*
41738  * Based on:
41739  * Ext JS Library 1.1.1
41740  * Copyright(c) 2006-2007, Ext JS, LLC.
41741  *
41742  * Originally Released Under LGPL - original licence link has changed is not relivant.
41743  *
41744  * Fork - LGPL
41745  * <script type="text/javascript">
41746  */
41747  
41748 /**
41749  * @class Roo.form.Layout
41750  * @extends Roo.Component
41751  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41752  * @constructor
41753  * @param {Object} config Configuration options
41754  */
41755 Roo.form.Layout = function(config){
41756     var xitems = [];
41757     if (config.items) {
41758         xitems = config.items;
41759         delete config.items;
41760     }
41761     Roo.form.Layout.superclass.constructor.call(this, config);
41762     this.stack = [];
41763     Roo.each(xitems, this.addxtype, this);
41764      
41765 };
41766
41767 Roo.extend(Roo.form.Layout, Roo.Component, {
41768     /**
41769      * @cfg {String/Object} autoCreate
41770      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41771      */
41772     /**
41773      * @cfg {String/Object/Function} style
41774      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41775      * a function which returns such a specification.
41776      */
41777     /**
41778      * @cfg {String} labelAlign
41779      * Valid values are "left," "top" and "right" (defaults to "left")
41780      */
41781     /**
41782      * @cfg {Number} labelWidth
41783      * Fixed width in pixels of all field labels (defaults to undefined)
41784      */
41785     /**
41786      * @cfg {Boolean} clear
41787      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41788      */
41789     clear : true,
41790     /**
41791      * @cfg {String} labelSeparator
41792      * The separator to use after field labels (defaults to ':')
41793      */
41794     labelSeparator : ':',
41795     /**
41796      * @cfg {Boolean} hideLabels
41797      * True to suppress the display of field labels in this layout (defaults to false)
41798      */
41799     hideLabels : false,
41800
41801     // private
41802     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41803     
41804     isLayout : true,
41805     
41806     // private
41807     onRender : function(ct, position){
41808         if(this.el){ // from markup
41809             this.el = Roo.get(this.el);
41810         }else {  // generate
41811             var cfg = this.getAutoCreate();
41812             this.el = ct.createChild(cfg, position);
41813         }
41814         if(this.style){
41815             this.el.applyStyles(this.style);
41816         }
41817         if(this.labelAlign){
41818             this.el.addClass('x-form-label-'+this.labelAlign);
41819         }
41820         if(this.hideLabels){
41821             this.labelStyle = "display:none";
41822             this.elementStyle = "padding-left:0;";
41823         }else{
41824             if(typeof this.labelWidth == 'number'){
41825                 this.labelStyle = "width:"+this.labelWidth+"px;";
41826                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41827             }
41828             if(this.labelAlign == 'top'){
41829                 this.labelStyle = "width:auto;";
41830                 this.elementStyle = "padding-left:0;";
41831             }
41832         }
41833         var stack = this.stack;
41834         var slen = stack.length;
41835         if(slen > 0){
41836             if(!this.fieldTpl){
41837                 var t = new Roo.Template(
41838                     '<div class="x-form-item {5}">',
41839                         '<label for="{0}" style="{2}">{1}{4}</label>',
41840                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41841                         '</div>',
41842                     '</div><div class="x-form-clear-left"></div>'
41843                 );
41844                 t.disableFormats = true;
41845                 t.compile();
41846                 Roo.form.Layout.prototype.fieldTpl = t;
41847             }
41848             for(var i = 0; i < slen; i++) {
41849                 if(stack[i].isFormField){
41850                     this.renderField(stack[i]);
41851                 }else{
41852                     this.renderComponent(stack[i]);
41853                 }
41854             }
41855         }
41856         if(this.clear){
41857             this.el.createChild({cls:'x-form-clear'});
41858         }
41859     },
41860
41861     // private
41862     renderField : function(f){
41863         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41864                f.id, //0
41865                f.fieldLabel, //1
41866                f.labelStyle||this.labelStyle||'', //2
41867                this.elementStyle||'', //3
41868                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41869                f.itemCls||this.itemCls||''  //5
41870        ], true).getPrevSibling());
41871     },
41872
41873     // private
41874     renderComponent : function(c){
41875         c.render(c.isLayout ? this.el : this.el.createChild());    
41876     },
41877     /**
41878      * Adds a object form elements (using the xtype property as the factory method.)
41879      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41880      * @param {Object} config 
41881      */
41882     addxtype : function(o)
41883     {
41884         // create the lement.
41885         o.form = this.form;
41886         var fe = Roo.factory(o, Roo.form);
41887         this.form.allItems.push(fe);
41888         this.stack.push(fe);
41889         
41890         if (fe.isFormField) {
41891             this.form.items.add(fe);
41892         }
41893          
41894         return fe;
41895     }
41896 });
41897
41898 /**
41899  * @class Roo.form.Column
41900  * @extends Roo.form.Layout
41901  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41902  * @constructor
41903  * @param {Object} config Configuration options
41904  */
41905 Roo.form.Column = function(config){
41906     Roo.form.Column.superclass.constructor.call(this, config);
41907 };
41908
41909 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41910     /**
41911      * @cfg {Number/String} width
41912      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41913      */
41914     /**
41915      * @cfg {String/Object} autoCreate
41916      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41917      */
41918
41919     // private
41920     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41921
41922     // private
41923     onRender : function(ct, position){
41924         Roo.form.Column.superclass.onRender.call(this, ct, position);
41925         if(this.width){
41926             this.el.setWidth(this.width);
41927         }
41928     }
41929 });
41930
41931
41932 /**
41933  * @class Roo.form.Row
41934  * @extends Roo.form.Layout
41935  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41936  * @constructor
41937  * @param {Object} config Configuration options
41938  */
41939
41940  
41941 Roo.form.Row = function(config){
41942     Roo.form.Row.superclass.constructor.call(this, config);
41943 };
41944  
41945 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41946       /**
41947      * @cfg {Number/String} width
41948      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41949      */
41950     /**
41951      * @cfg {Number/String} height
41952      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41953      */
41954     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41955     
41956     padWidth : 20,
41957     // private
41958     onRender : function(ct, position){
41959         //console.log('row render');
41960         if(!this.rowTpl){
41961             var t = new Roo.Template(
41962                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41963                     '<label for="{0}" style="{2}">{1}{4}</label>',
41964                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41965                     '</div>',
41966                 '</div>'
41967             );
41968             t.disableFormats = true;
41969             t.compile();
41970             Roo.form.Layout.prototype.rowTpl = t;
41971         }
41972         this.fieldTpl = this.rowTpl;
41973         
41974         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41975         var labelWidth = 100;
41976         
41977         if ((this.labelAlign != 'top')) {
41978             if (typeof this.labelWidth == 'number') {
41979                 labelWidth = this.labelWidth
41980             }
41981             this.padWidth =  20 + labelWidth;
41982             
41983         }
41984         
41985         Roo.form.Column.superclass.onRender.call(this, ct, position);
41986         if(this.width){
41987             this.el.setWidth(this.width);
41988         }
41989         if(this.height){
41990             this.el.setHeight(this.height);
41991         }
41992     },
41993     
41994     // private
41995     renderField : function(f){
41996         f.fieldEl = this.fieldTpl.append(this.el, [
41997                f.id, f.fieldLabel,
41998                f.labelStyle||this.labelStyle||'',
41999                this.elementStyle||'',
42000                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42001                f.itemCls||this.itemCls||'',
42002                f.width ? f.width + this.padWidth : 160 + this.padWidth
42003        ],true);
42004     }
42005 });
42006  
42007
42008 /**
42009  * @class Roo.form.FieldSet
42010  * @extends Roo.form.Layout
42011  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42012  * @constructor
42013  * @param {Object} config Configuration options
42014  */
42015 Roo.form.FieldSet = function(config){
42016     Roo.form.FieldSet.superclass.constructor.call(this, config);
42017 };
42018
42019 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42020     /**
42021      * @cfg {String} legend
42022      * The text to display as the legend for the FieldSet (defaults to '')
42023      */
42024     /**
42025      * @cfg {String/Object} autoCreate
42026      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42027      */
42028
42029     // private
42030     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42031
42032     // private
42033     onRender : function(ct, position){
42034         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42035         if(this.legend){
42036             this.setLegend(this.legend);
42037         }
42038     },
42039
42040     // private
42041     setLegend : function(text){
42042         if(this.rendered){
42043             this.el.child('legend').update(text);
42044         }
42045     }
42046 });/*
42047  * Based on:
42048  * Ext JS Library 1.1.1
42049  * Copyright(c) 2006-2007, Ext JS, LLC.
42050  *
42051  * Originally Released Under LGPL - original licence link has changed is not relivant.
42052  *
42053  * Fork - LGPL
42054  * <script type="text/javascript">
42055  */
42056 /**
42057  * @class Roo.form.VTypes
42058  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42059  * @singleton
42060  */
42061 Roo.form.VTypes = function(){
42062     // closure these in so they are only created once.
42063     var alpha = /^[a-zA-Z_]+$/;
42064     var alphanum = /^[a-zA-Z0-9_]+$/;
42065     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42066     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42067
42068     // All these messages and functions are configurable
42069     return {
42070         /**
42071          * The function used to validate email addresses
42072          * @param {String} value The email address
42073          */
42074         'email' : function(v){
42075             return email.test(v);
42076         },
42077         /**
42078          * The error text to display when the email validation function returns false
42079          * @type String
42080          */
42081         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42082         /**
42083          * The keystroke filter mask to be applied on email input
42084          * @type RegExp
42085          */
42086         'emailMask' : /[a-z0-9_\.\-@]/i,
42087
42088         /**
42089          * The function used to validate URLs
42090          * @param {String} value The URL
42091          */
42092         'url' : function(v){
42093             return url.test(v);
42094         },
42095         /**
42096          * The error text to display when the url validation function returns false
42097          * @type String
42098          */
42099         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42100         
42101         /**
42102          * The function used to validate alpha values
42103          * @param {String} value The value
42104          */
42105         'alpha' : function(v){
42106             return alpha.test(v);
42107         },
42108         /**
42109          * The error text to display when the alpha validation function returns false
42110          * @type String
42111          */
42112         'alphaText' : 'This field should only contain letters and _',
42113         /**
42114          * The keystroke filter mask to be applied on alpha input
42115          * @type RegExp
42116          */
42117         'alphaMask' : /[a-z_]/i,
42118
42119         /**
42120          * The function used to validate alphanumeric values
42121          * @param {String} value The value
42122          */
42123         'alphanum' : function(v){
42124             return alphanum.test(v);
42125         },
42126         /**
42127          * The error text to display when the alphanumeric validation function returns false
42128          * @type String
42129          */
42130         'alphanumText' : 'This field should only contain letters, numbers and _',
42131         /**
42132          * The keystroke filter mask to be applied on alphanumeric input
42133          * @type RegExp
42134          */
42135         'alphanumMask' : /[a-z0-9_]/i
42136     };
42137 }();//<script type="text/javascript">
42138
42139 /**
42140  * @class Roo.form.FCKeditor
42141  * @extends Roo.form.TextArea
42142  * Wrapper around the FCKEditor http://www.fckeditor.net
42143  * @constructor
42144  * Creates a new FCKeditor
42145  * @param {Object} config Configuration options
42146  */
42147 Roo.form.FCKeditor = function(config){
42148     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42149     this.addEvents({
42150          /**
42151          * @event editorinit
42152          * Fired when the editor is initialized - you can add extra handlers here..
42153          * @param {FCKeditor} this
42154          * @param {Object} the FCK object.
42155          */
42156         editorinit : true
42157     });
42158     
42159     
42160 };
42161 Roo.form.FCKeditor.editors = { };
42162 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42163 {
42164     //defaultAutoCreate : {
42165     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42166     //},
42167     // private
42168     /**
42169      * @cfg {Object} fck options - see fck manual for details.
42170      */
42171     fckconfig : false,
42172     
42173     /**
42174      * @cfg {Object} fck toolbar set (Basic or Default)
42175      */
42176     toolbarSet : 'Basic',
42177     /**
42178      * @cfg {Object} fck BasePath
42179      */ 
42180     basePath : '/fckeditor/',
42181     
42182     
42183     frame : false,
42184     
42185     value : '',
42186     
42187    
42188     onRender : function(ct, position)
42189     {
42190         if(!this.el){
42191             this.defaultAutoCreate = {
42192                 tag: "textarea",
42193                 style:"width:300px;height:60px;",
42194                 autocomplete: "off"
42195             };
42196         }
42197         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42198         /*
42199         if(this.grow){
42200             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42201             if(this.preventScrollbars){
42202                 this.el.setStyle("overflow", "hidden");
42203             }
42204             this.el.setHeight(this.growMin);
42205         }
42206         */
42207         //console.log('onrender' + this.getId() );
42208         Roo.form.FCKeditor.editors[this.getId()] = this;
42209          
42210
42211         this.replaceTextarea() ;
42212         
42213     },
42214     
42215     getEditor : function() {
42216         return this.fckEditor;
42217     },
42218     /**
42219      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42220      * @param {Mixed} value The value to set
42221      */
42222     
42223     
42224     setValue : function(value)
42225     {
42226         //console.log('setValue: ' + value);
42227         
42228         if(typeof(value) == 'undefined') { // not sure why this is happending...
42229             return;
42230         }
42231         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42232         
42233         //if(!this.el || !this.getEditor()) {
42234         //    this.value = value;
42235             //this.setValue.defer(100,this,[value]);    
42236         //    return;
42237         //} 
42238         
42239         if(!this.getEditor()) {
42240             return;
42241         }
42242         
42243         this.getEditor().SetData(value);
42244         
42245         //
42246
42247     },
42248
42249     /**
42250      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42251      * @return {Mixed} value The field value
42252      */
42253     getValue : function()
42254     {
42255         
42256         if (this.frame && this.frame.dom.style.display == 'none') {
42257             return Roo.form.FCKeditor.superclass.getValue.call(this);
42258         }
42259         
42260         if(!this.el || !this.getEditor()) {
42261            
42262            // this.getValue.defer(100,this); 
42263             return this.value;
42264         }
42265        
42266         
42267         var value=this.getEditor().GetData();
42268         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42269         return Roo.form.FCKeditor.superclass.getValue.call(this);
42270         
42271
42272     },
42273
42274     /**
42275      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42276      * @return {Mixed} value The field value
42277      */
42278     getRawValue : function()
42279     {
42280         if (this.frame && this.frame.dom.style.display == 'none') {
42281             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42282         }
42283         
42284         if(!this.el || !this.getEditor()) {
42285             //this.getRawValue.defer(100,this); 
42286             return this.value;
42287             return;
42288         }
42289         
42290         
42291         
42292         var value=this.getEditor().GetData();
42293         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42294         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42295          
42296     },
42297     
42298     setSize : function(w,h) {
42299         
42300         
42301         
42302         //if (this.frame && this.frame.dom.style.display == 'none') {
42303         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42304         //    return;
42305         //}
42306         //if(!this.el || !this.getEditor()) {
42307         //    this.setSize.defer(100,this, [w,h]); 
42308         //    return;
42309         //}
42310         
42311         
42312         
42313         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42314         
42315         this.frame.dom.setAttribute('width', w);
42316         this.frame.dom.setAttribute('height', h);
42317         this.frame.setSize(w,h);
42318         
42319     },
42320     
42321     toggleSourceEdit : function(value) {
42322         
42323       
42324          
42325         this.el.dom.style.display = value ? '' : 'none';
42326         this.frame.dom.style.display = value ?  'none' : '';
42327         
42328     },
42329     
42330     
42331     focus: function(tag)
42332     {
42333         if (this.frame.dom.style.display == 'none') {
42334             return Roo.form.FCKeditor.superclass.focus.call(this);
42335         }
42336         if(!this.el || !this.getEditor()) {
42337             this.focus.defer(100,this, [tag]); 
42338             return;
42339         }
42340         
42341         
42342         
42343         
42344         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42345         this.getEditor().Focus();
42346         if (tgs.length) {
42347             if (!this.getEditor().Selection.GetSelection()) {
42348                 this.focus.defer(100,this, [tag]); 
42349                 return;
42350             }
42351             
42352             
42353             var r = this.getEditor().EditorDocument.createRange();
42354             r.setStart(tgs[0],0);
42355             r.setEnd(tgs[0],0);
42356             this.getEditor().Selection.GetSelection().removeAllRanges();
42357             this.getEditor().Selection.GetSelection().addRange(r);
42358             this.getEditor().Focus();
42359         }
42360         
42361     },
42362     
42363     
42364     
42365     replaceTextarea : function()
42366     {
42367         if ( document.getElementById( this.getId() + '___Frame' ) )
42368             return ;
42369         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42370         //{
42371             // We must check the elements firstly using the Id and then the name.
42372         var oTextarea = document.getElementById( this.getId() );
42373         
42374         var colElementsByName = document.getElementsByName( this.getId() ) ;
42375          
42376         oTextarea.style.display = 'none' ;
42377
42378         if ( oTextarea.tabIndex ) {            
42379             this.TabIndex = oTextarea.tabIndex ;
42380         }
42381         
42382         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42383         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42384         this.frame = Roo.get(this.getId() + '___Frame')
42385     },
42386     
42387     _getConfigHtml : function()
42388     {
42389         var sConfig = '' ;
42390
42391         for ( var o in this.fckconfig ) {
42392             sConfig += sConfig.length > 0  ? '&amp;' : '';
42393             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42394         }
42395
42396         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42397     },
42398     
42399     
42400     _getIFrameHtml : function()
42401     {
42402         var sFile = 'fckeditor.html' ;
42403         /* no idea what this is about..
42404         try
42405         {
42406             if ( (/fcksource=true/i).test( window.top.location.search ) )
42407                 sFile = 'fckeditor.original.html' ;
42408         }
42409         catch (e) { 
42410         */
42411
42412         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42413         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42414         
42415         
42416         var html = '<iframe id="' + this.getId() +
42417             '___Frame" src="' + sLink +
42418             '" width="' + this.width +
42419             '" height="' + this.height + '"' +
42420             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42421             ' frameborder="0" scrolling="no"></iframe>' ;
42422
42423         return html ;
42424     },
42425     
42426     _insertHtmlBefore : function( html, element )
42427     {
42428         if ( element.insertAdjacentHTML )       {
42429             // IE
42430             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42431         } else { // Gecko
42432             var oRange = document.createRange() ;
42433             oRange.setStartBefore( element ) ;
42434             var oFragment = oRange.createContextualFragment( html );
42435             element.parentNode.insertBefore( oFragment, element ) ;
42436         }
42437     }
42438     
42439     
42440   
42441     
42442     
42443     
42444     
42445
42446 });
42447
42448 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42449
42450 function FCKeditor_OnComplete(editorInstance){
42451     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42452     f.fckEditor = editorInstance;
42453     //console.log("loaded");
42454     f.fireEvent('editorinit', f, editorInstance);
42455
42456   
42457
42458  
42459
42460
42461
42462
42463
42464
42465
42466
42467
42468
42469
42470
42471
42472
42473
42474 //<script type="text/javascript">
42475 /**
42476  * @class Roo.form.GridField
42477  * @extends Roo.form.Field
42478  * Embed a grid (or editable grid into a form)
42479  * STATUS ALPHA
42480  * 
42481  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42482  * it needs 
42483  * xgrid.store = Roo.data.Store
42484  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42485  * xgrid.store.reader = Roo.data.JsonReader 
42486  * 
42487  * 
42488  * @constructor
42489  * Creates a new GridField
42490  * @param {Object} config Configuration options
42491  */
42492 Roo.form.GridField = function(config){
42493     Roo.form.GridField.superclass.constructor.call(this, config);
42494      
42495 };
42496
42497 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42498     /**
42499      * @cfg {Number} width  - used to restrict width of grid..
42500      */
42501     width : 100,
42502     /**
42503      * @cfg {Number} height - used to restrict height of grid..
42504      */
42505     height : 50,
42506      /**
42507      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42508          * 
42509          *}
42510      */
42511     xgrid : false, 
42512     /**
42513      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42514      * {tag: "input", type: "checkbox", autocomplete: "off"})
42515      */
42516    // defaultAutoCreate : { tag: 'div' },
42517     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42518     /**
42519      * @cfg {String} addTitle Text to include for adding a title.
42520      */
42521     addTitle : false,
42522     //
42523     onResize : function(){
42524         Roo.form.Field.superclass.onResize.apply(this, arguments);
42525     },
42526
42527     initEvents : function(){
42528         // Roo.form.Checkbox.superclass.initEvents.call(this);
42529         // has no events...
42530        
42531     },
42532
42533
42534     getResizeEl : function(){
42535         return this.wrap;
42536     },
42537
42538     getPositionEl : function(){
42539         return this.wrap;
42540     },
42541
42542     // private
42543     onRender : function(ct, position){
42544         
42545         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42546         var style = this.style;
42547         delete this.style;
42548         
42549         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42550         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42551         this.viewEl = this.wrap.createChild({ tag: 'div' });
42552         if (style) {
42553             this.viewEl.applyStyles(style);
42554         }
42555         if (this.width) {
42556             this.viewEl.setWidth(this.width);
42557         }
42558         if (this.height) {
42559             this.viewEl.setHeight(this.height);
42560         }
42561         //if(this.inputValue !== undefined){
42562         //this.setValue(this.value);
42563         
42564         
42565         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42566         
42567         
42568         this.grid.render();
42569         this.grid.getDataSource().on('remove', this.refreshValue, this);
42570         this.grid.getDataSource().on('update', this.refreshValue, this);
42571         this.grid.on('afteredit', this.refreshValue, this);
42572  
42573     },
42574      
42575     
42576     /**
42577      * Sets the value of the item. 
42578      * @param {String} either an object  or a string..
42579      */
42580     setValue : function(v){
42581         //this.value = v;
42582         v = v || []; // empty set..
42583         // this does not seem smart - it really only affects memoryproxy grids..
42584         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42585             var ds = this.grid.getDataSource();
42586             // assumes a json reader..
42587             var data = {}
42588             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42589             ds.loadData( data);
42590         }
42591         Roo.form.GridField.superclass.setValue.call(this, v);
42592         this.refreshValue();
42593         // should load data in the grid really....
42594     },
42595     
42596     // private
42597     refreshValue: function() {
42598          var val = [];
42599         this.grid.getDataSource().each(function(r) {
42600             val.push(r.data);
42601         });
42602         this.el.dom.value = Roo.encode(val);
42603     }
42604     
42605      
42606     
42607     
42608 });/*
42609  * Based on:
42610  * Ext JS Library 1.1.1
42611  * Copyright(c) 2006-2007, Ext JS, LLC.
42612  *
42613  * Originally Released Under LGPL - original licence link has changed is not relivant.
42614  *
42615  * Fork - LGPL
42616  * <script type="text/javascript">
42617  */
42618 /**
42619  * @class Roo.form.DisplayField
42620  * @extends Roo.form.Field
42621  * A generic Field to display non-editable data.
42622  * @constructor
42623  * Creates a new Display Field item.
42624  * @param {Object} config Configuration options
42625  */
42626 Roo.form.DisplayField = function(config){
42627     Roo.form.DisplayField.superclass.constructor.call(this, config);
42628     
42629 };
42630
42631 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42632     inputType:      'hidden',
42633     allowBlank:     true,
42634     readOnly:         true,
42635     
42636  
42637     /**
42638      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42639      */
42640     focusClass : undefined,
42641     /**
42642      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42643      */
42644     fieldClass: 'x-form-field',
42645     
42646      /**
42647      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42648      */
42649     valueRenderer: undefined,
42650     
42651     width: 100,
42652     /**
42653      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42654      * {tag: "input", type: "checkbox", autocomplete: "off"})
42655      */
42656      
42657  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42658
42659     onResize : function(){
42660         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42661         
42662     },
42663
42664     initEvents : function(){
42665         // Roo.form.Checkbox.superclass.initEvents.call(this);
42666         // has no events...
42667        
42668     },
42669
42670
42671     getResizeEl : function(){
42672         return this.wrap;
42673     },
42674
42675     getPositionEl : function(){
42676         return this.wrap;
42677     },
42678
42679     // private
42680     onRender : function(ct, position){
42681         
42682         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42683         //if(this.inputValue !== undefined){
42684         this.wrap = this.el.wrap();
42685         
42686         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42687         
42688         if (this.bodyStyle) {
42689             this.viewEl.applyStyles(this.bodyStyle);
42690         }
42691         //this.viewEl.setStyle('padding', '2px');
42692         
42693         this.setValue(this.value);
42694         
42695     },
42696 /*
42697     // private
42698     initValue : Roo.emptyFn,
42699
42700   */
42701
42702         // private
42703     onClick : function(){
42704         
42705     },
42706
42707     /**
42708      * Sets the checked state of the checkbox.
42709      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42710      */
42711     setValue : function(v){
42712         this.value = v;
42713         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42714         // this might be called before we have a dom element..
42715         if (!this.viewEl) {
42716             return;
42717         }
42718         this.viewEl.dom.innerHTML = html;
42719         Roo.form.DisplayField.superclass.setValue.call(this, v);
42720
42721     }
42722 });/*
42723  * 
42724  * Licence- LGPL
42725  * 
42726  */
42727
42728 /**
42729  * @class Roo.form.DayPicker
42730  * @extends Roo.form.Field
42731  * A Day picker show [M] [T] [W] ....
42732  * @constructor
42733  * Creates a new Day Picker
42734  * @param {Object} config Configuration options
42735  */
42736 Roo.form.DayPicker= function(config){
42737     Roo.form.DayPicker.superclass.constructor.call(this, config);
42738      
42739 };
42740
42741 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42742     /**
42743      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42744      */
42745     focusClass : undefined,
42746     /**
42747      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42748      */
42749     fieldClass: "x-form-field",
42750    
42751     /**
42752      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42753      * {tag: "input", type: "checkbox", autocomplete: "off"})
42754      */
42755     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42756     
42757    
42758     actionMode : 'viewEl', 
42759     //
42760     // private
42761  
42762     inputType : 'hidden',
42763     
42764      
42765     inputElement: false, // real input element?
42766     basedOn: false, // ????
42767     
42768     isFormField: true, // not sure where this is needed!!!!
42769
42770     onResize : function(){
42771         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42772         if(!this.boxLabel){
42773             this.el.alignTo(this.wrap, 'c-c');
42774         }
42775     },
42776
42777     initEvents : function(){
42778         Roo.form.Checkbox.superclass.initEvents.call(this);
42779         this.el.on("click", this.onClick,  this);
42780         this.el.on("change", this.onClick,  this);
42781     },
42782
42783
42784     getResizeEl : function(){
42785         return this.wrap;
42786     },
42787
42788     getPositionEl : function(){
42789         return this.wrap;
42790     },
42791
42792     
42793     // private
42794     onRender : function(ct, position){
42795         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42796        
42797         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42798         
42799         var r1 = '<table><tr>';
42800         var r2 = '<tr class="x-form-daypick-icons">';
42801         for (var i=0; i < 7; i++) {
42802             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42803             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42804         }
42805         
42806         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42807         viewEl.select('img').on('click', this.onClick, this);
42808         this.viewEl = viewEl;   
42809         
42810         
42811         // this will not work on Chrome!!!
42812         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42813         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42814         
42815         
42816           
42817
42818     },
42819
42820     // private
42821     initValue : Roo.emptyFn,
42822
42823     /**
42824      * Returns the checked state of the checkbox.
42825      * @return {Boolean} True if checked, else false
42826      */
42827     getValue : function(){
42828         return this.el.dom.value;
42829         
42830     },
42831
42832         // private
42833     onClick : function(e){ 
42834         //this.setChecked(!this.checked);
42835         Roo.get(e.target).toggleClass('x-menu-item-checked');
42836         this.refreshValue();
42837         //if(this.el.dom.checked != this.checked){
42838         //    this.setValue(this.el.dom.checked);
42839        // }
42840     },
42841     
42842     // private
42843     refreshValue : function()
42844     {
42845         var val = '';
42846         this.viewEl.select('img',true).each(function(e,i,n)  {
42847             val += e.is(".x-menu-item-checked") ? String(n) : '';
42848         });
42849         this.setValue(val, true);
42850     },
42851
42852     /**
42853      * Sets the checked state of the checkbox.
42854      * On is always based on a string comparison between inputValue and the param.
42855      * @param {Boolean/String} value - the value to set 
42856      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42857      */
42858     setValue : function(v,suppressEvent){
42859         if (!this.el.dom) {
42860             return;
42861         }
42862         var old = this.el.dom.value ;
42863         this.el.dom.value = v;
42864         if (suppressEvent) {
42865             return ;
42866         }
42867          
42868         // update display..
42869         this.viewEl.select('img',true).each(function(e,i,n)  {
42870             
42871             var on = e.is(".x-menu-item-checked");
42872             var newv = v.indexOf(String(n)) > -1;
42873             if (on != newv) {
42874                 e.toggleClass('x-menu-item-checked');
42875             }
42876             
42877         });
42878         
42879         
42880         this.fireEvent('change', this, v, old);
42881         
42882         
42883     },
42884    
42885     // handle setting of hidden value by some other method!!?!?
42886     setFromHidden: function()
42887     {
42888         if(!this.el){
42889             return;
42890         }
42891         //console.log("SET FROM HIDDEN");
42892         //alert('setFrom hidden');
42893         this.setValue(this.el.dom.value);
42894     },
42895     
42896     onDestroy : function()
42897     {
42898         if(this.viewEl){
42899             Roo.get(this.viewEl).remove();
42900         }
42901          
42902         Roo.form.DayPicker.superclass.onDestroy.call(this);
42903     }
42904
42905 });//<script type="text/javasscript">
42906  
42907
42908 /**
42909  * @class Roo.DDView
42910  * A DnD enabled version of Roo.View.
42911  * @param {Element/String} container The Element in which to create the View.
42912  * @param {String} tpl The template string used to create the markup for each element of the View
42913  * @param {Object} config The configuration properties. These include all the config options of
42914  * {@link Roo.View} plus some specific to this class.<br>
42915  * <p>
42916  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42917  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42918  * <p>
42919  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42920 .x-view-drag-insert-above {
42921         border-top:1px dotted #3366cc;
42922 }
42923 .x-view-drag-insert-below {
42924         border-bottom:1px dotted #3366cc;
42925 }
42926 </code></pre>
42927  * 
42928  */
42929  
42930 Roo.DDView = function(container, tpl, config) {
42931     Roo.DDView.superclass.constructor.apply(this, arguments);
42932     this.getEl().setStyle("outline", "0px none");
42933     this.getEl().unselectable();
42934     if (this.dragGroup) {
42935                 this.setDraggable(this.dragGroup.split(","));
42936     }
42937     if (this.dropGroup) {
42938                 this.setDroppable(this.dropGroup.split(","));
42939     }
42940     if (this.deletable) {
42941         this.setDeletable();
42942     }
42943     this.isDirtyFlag = false;
42944         this.addEvents({
42945                 "drop" : true
42946         });
42947 };
42948
42949 Roo.extend(Roo.DDView, Roo.View, {
42950 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42951 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42952 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42953 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42954
42955         isFormField: true,
42956
42957         reset: Roo.emptyFn,
42958         
42959         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42960
42961         validate: function() {
42962                 return true;
42963         },
42964         
42965         destroy: function() {
42966                 this.purgeListeners();
42967                 this.getEl.removeAllListeners();
42968                 this.getEl().remove();
42969                 if (this.dragZone) {
42970                         if (this.dragZone.destroy) {
42971                                 this.dragZone.destroy();
42972                         }
42973                 }
42974                 if (this.dropZone) {
42975                         if (this.dropZone.destroy) {
42976                                 this.dropZone.destroy();
42977                         }
42978                 }
42979         },
42980
42981 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42982         getName: function() {
42983                 return this.name;
42984         },
42985
42986 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42987         setValue: function(v) {
42988                 if (!this.store) {
42989                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42990                 }
42991                 var data = {};
42992                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42993                 this.store.proxy = new Roo.data.MemoryProxy(data);
42994                 this.store.load();
42995         },
42996
42997 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42998         getValue: function() {
42999                 var result = '(';
43000                 this.store.each(function(rec) {
43001                         result += rec.id + ',';
43002                 });
43003                 return result.substr(0, result.length - 1) + ')';
43004         },
43005         
43006         getIds: function() {
43007                 var i = 0, result = new Array(this.store.getCount());
43008                 this.store.each(function(rec) {
43009                         result[i++] = rec.id;
43010                 });
43011                 return result;
43012         },
43013         
43014         isDirty: function() {
43015                 return this.isDirtyFlag;
43016         },
43017
43018 /**
43019  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43020  *      whole Element becomes the target, and this causes the drop gesture to append.
43021  */
43022     getTargetFromEvent : function(e) {
43023                 var target = e.getTarget();
43024                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43025                 target = target.parentNode;
43026                 }
43027                 if (!target) {
43028                         target = this.el.dom.lastChild || this.el.dom;
43029                 }
43030                 return target;
43031     },
43032
43033 /**
43034  *      Create the drag data which consists of an object which has the property "ddel" as
43035  *      the drag proxy element. 
43036  */
43037     getDragData : function(e) {
43038         var target = this.findItemFromChild(e.getTarget());
43039                 if(target) {
43040                         this.handleSelection(e);
43041                         var selNodes = this.getSelectedNodes();
43042             var dragData = {
43043                 source: this,
43044                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43045                 nodes: selNodes,
43046                 records: []
43047                         };
43048                         var selectedIndices = this.getSelectedIndexes();
43049                         for (var i = 0; i < selectedIndices.length; i++) {
43050                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43051                         }
43052                         if (selNodes.length == 1) {
43053                                 dragData.ddel = target.cloneNode(true); // the div element
43054                         } else {
43055                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43056                                 div.className = 'multi-proxy';
43057                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43058                                         div.appendChild(selNodes[i].cloneNode(true));
43059                                 }
43060                                 dragData.ddel = div;
43061                         }
43062             //console.log(dragData)
43063             //console.log(dragData.ddel.innerHTML)
43064                         return dragData;
43065                 }
43066         //console.log('nodragData')
43067                 return false;
43068     },
43069     
43070 /**     Specify to which ddGroup items in this DDView may be dragged. */
43071     setDraggable: function(ddGroup) {
43072         if (ddGroup instanceof Array) {
43073                 Roo.each(ddGroup, this.setDraggable, this);
43074                 return;
43075         }
43076         if (this.dragZone) {
43077                 this.dragZone.addToGroup(ddGroup);
43078         } else {
43079                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43080                                 containerScroll: true,
43081                                 ddGroup: ddGroup 
43082
43083                         });
43084 //                      Draggability implies selection. DragZone's mousedown selects the element.
43085                         if (!this.multiSelect) { this.singleSelect = true; }
43086
43087 //                      Wire the DragZone's handlers up to methods in *this*
43088                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43089                 }
43090     },
43091
43092 /**     Specify from which ddGroup this DDView accepts drops. */
43093     setDroppable: function(ddGroup) {
43094         if (ddGroup instanceof Array) {
43095                 Roo.each(ddGroup, this.setDroppable, this);
43096                 return;
43097         }
43098         if (this.dropZone) {
43099                 this.dropZone.addToGroup(ddGroup);
43100         } else {
43101                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43102                                 containerScroll: true,
43103                                 ddGroup: ddGroup
43104                         });
43105
43106 //                      Wire the DropZone's handlers up to methods in *this*
43107                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43108                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43109                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43110                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43111                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43112                 }
43113     },
43114
43115 /**     Decide whether to drop above or below a View node. */
43116     getDropPoint : function(e, n, dd){
43117         if (n == this.el.dom) { return "above"; }
43118                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43119                 var c = t + (b - t) / 2;
43120                 var y = Roo.lib.Event.getPageY(e);
43121                 if(y <= c) {
43122                         return "above";
43123                 }else{
43124                         return "below";
43125                 }
43126     },
43127
43128     onNodeEnter : function(n, dd, e, data){
43129                 return false;
43130     },
43131     
43132     onNodeOver : function(n, dd, e, data){
43133                 var pt = this.getDropPoint(e, n, dd);
43134                 // set the insert point style on the target node
43135                 var dragElClass = this.dropNotAllowed;
43136                 if (pt) {
43137                         var targetElClass;
43138                         if (pt == "above"){
43139                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43140                                 targetElClass = "x-view-drag-insert-above";
43141                         } else {
43142                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43143                                 targetElClass = "x-view-drag-insert-below";
43144                         }
43145                         if (this.lastInsertClass != targetElClass){
43146                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43147                                 this.lastInsertClass = targetElClass;
43148                         }
43149                 }
43150                 return dragElClass;
43151         },
43152
43153     onNodeOut : function(n, dd, e, data){
43154                 this.removeDropIndicators(n);
43155     },
43156
43157     onNodeDrop : function(n, dd, e, data){
43158         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43159                 return false;
43160         }
43161         var pt = this.getDropPoint(e, n, dd);
43162                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43163                 if (pt == "below") { insertAt++; }
43164                 for (var i = 0; i < data.records.length; i++) {
43165                         var r = data.records[i];
43166                         var dup = this.store.getById(r.id);
43167                         if (dup && (dd != this.dragZone)) {
43168                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43169                         } else {
43170                                 if (data.copy) {
43171                                         this.store.insert(insertAt++, r.copy());
43172                                 } else {
43173                                         data.source.isDirtyFlag = true;
43174                                         r.store.remove(r);
43175                                         this.store.insert(insertAt++, r);
43176                                 }
43177                                 this.isDirtyFlag = true;
43178                         }
43179                 }
43180                 this.dragZone.cachedTarget = null;
43181                 return true;
43182     },
43183
43184     removeDropIndicators : function(n){
43185                 if(n){
43186                         Roo.fly(n).removeClass([
43187                                 "x-view-drag-insert-above",
43188                                 "x-view-drag-insert-below"]);
43189                         this.lastInsertClass = "_noclass";
43190                 }
43191     },
43192
43193 /**
43194  *      Utility method. Add a delete option to the DDView's context menu.
43195  *      @param {String} imageUrl The URL of the "delete" icon image.
43196  */
43197         setDeletable: function(imageUrl) {
43198                 if (!this.singleSelect && !this.multiSelect) {
43199                         this.singleSelect = true;
43200                 }
43201                 var c = this.getContextMenu();
43202                 this.contextMenu.on("itemclick", function(item) {
43203                         switch (item.id) {
43204                                 case "delete":
43205                                         this.remove(this.getSelectedIndexes());
43206                                         break;
43207                         }
43208                 }, this);
43209                 this.contextMenu.add({
43210                         icon: imageUrl,
43211                         id: "delete",
43212                         text: 'Delete'
43213                 });
43214         },
43215         
43216 /**     Return the context menu for this DDView. */
43217         getContextMenu: function() {
43218                 if (!this.contextMenu) {
43219 //                      Create the View's context menu
43220                         this.contextMenu = new Roo.menu.Menu({
43221                                 id: this.id + "-contextmenu"
43222                         });
43223                         this.el.on("contextmenu", this.showContextMenu, this);
43224                 }
43225                 return this.contextMenu;
43226         },
43227         
43228         disableContextMenu: function() {
43229                 if (this.contextMenu) {
43230                         this.el.un("contextmenu", this.showContextMenu, this);
43231                 }
43232         },
43233
43234         showContextMenu: function(e, item) {
43235         item = this.findItemFromChild(e.getTarget());
43236                 if (item) {
43237                         e.stopEvent();
43238                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43239                         this.contextMenu.showAt(e.getXY());
43240             }
43241     },
43242
43243 /**
43244  *      Remove {@link Roo.data.Record}s at the specified indices.
43245  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43246  */
43247     remove: function(selectedIndices) {
43248                 selectedIndices = [].concat(selectedIndices);
43249                 for (var i = 0; i < selectedIndices.length; i++) {
43250                         var rec = this.store.getAt(selectedIndices[i]);
43251                         this.store.remove(rec);
43252                 }
43253     },
43254
43255 /**
43256  *      Double click fires the event, but also, if this is draggable, and there is only one other
43257  *      related DropZone, it transfers the selected node.
43258  */
43259     onDblClick : function(e){
43260         var item = this.findItemFromChild(e.getTarget());
43261         if(item){
43262             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43263                 return false;
43264             }
43265             if (this.dragGroup) {
43266                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43267                     while (targets.indexOf(this.dropZone) > -1) {
43268                             targets.remove(this.dropZone);
43269                                 }
43270                     if (targets.length == 1) {
43271                                         this.dragZone.cachedTarget = null;
43272                         var el = Roo.get(targets[0].getEl());
43273                         var box = el.getBox(true);
43274                         targets[0].onNodeDrop(el.dom, {
43275                                 target: el.dom,
43276                                 xy: [box.x, box.y + box.height - 1]
43277                         }, null, this.getDragData(e));
43278                     }
43279                 }
43280         }
43281     },
43282     
43283     handleSelection: function(e) {
43284                 this.dragZone.cachedTarget = null;
43285         var item = this.findItemFromChild(e.getTarget());
43286         if (!item) {
43287                 this.clearSelections(true);
43288                 return;
43289         }
43290                 if (item && (this.multiSelect || this.singleSelect)){
43291                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43292                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43293                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43294                                 this.unselect(item);
43295                         } else {
43296                                 this.select(item, this.multiSelect && e.ctrlKey);
43297                                 this.lastSelection = item;
43298                         }
43299                 }
43300     },
43301
43302     onItemClick : function(item, index, e){
43303                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43304                         return false;
43305                 }
43306                 return true;
43307     },
43308
43309     unselect : function(nodeInfo, suppressEvent){
43310                 var node = this.getNode(nodeInfo);
43311                 if(node && this.isSelected(node)){
43312                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43313                                 Roo.fly(node).removeClass(this.selectedClass);
43314                                 this.selections.remove(node);
43315                                 if(!suppressEvent){
43316                                         this.fireEvent("selectionchange", this, this.selections);
43317                                 }
43318                         }
43319                 }
43320     }
43321 });
43322 /*
43323  * Based on:
43324  * Ext JS Library 1.1.1
43325  * Copyright(c) 2006-2007, Ext JS, LLC.
43326  *
43327  * Originally Released Under LGPL - original licence link has changed is not relivant.
43328  *
43329  * Fork - LGPL
43330  * <script type="text/javascript">
43331  */
43332  
43333 /**
43334  * @class Roo.LayoutManager
43335  * @extends Roo.util.Observable
43336  * Base class for layout managers.
43337  */
43338 Roo.LayoutManager = function(container, config){
43339     Roo.LayoutManager.superclass.constructor.call(this);
43340     this.el = Roo.get(container);
43341     // ie scrollbar fix
43342     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43343         document.body.scroll = "no";
43344     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43345         this.el.position('relative');
43346     }
43347     this.id = this.el.id;
43348     this.el.addClass("x-layout-container");
43349     /** false to disable window resize monitoring @type Boolean */
43350     this.monitorWindowResize = true;
43351     this.regions = {};
43352     this.addEvents({
43353         /**
43354          * @event layout
43355          * Fires when a layout is performed. 
43356          * @param {Roo.LayoutManager} this
43357          */
43358         "layout" : true,
43359         /**
43360          * @event regionresized
43361          * Fires when the user resizes a region. 
43362          * @param {Roo.LayoutRegion} region The resized region
43363          * @param {Number} newSize The new size (width for east/west, height for north/south)
43364          */
43365         "regionresized" : true,
43366         /**
43367          * @event regioncollapsed
43368          * Fires when a region is collapsed. 
43369          * @param {Roo.LayoutRegion} region The collapsed region
43370          */
43371         "regioncollapsed" : true,
43372         /**
43373          * @event regionexpanded
43374          * Fires when a region is expanded.  
43375          * @param {Roo.LayoutRegion} region The expanded region
43376          */
43377         "regionexpanded" : true
43378     });
43379     this.updating = false;
43380     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43381 };
43382
43383 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43384     /**
43385      * Returns true if this layout is currently being updated
43386      * @return {Boolean}
43387      */
43388     isUpdating : function(){
43389         return this.updating; 
43390     },
43391     
43392     /**
43393      * Suspend the LayoutManager from doing auto-layouts while
43394      * making multiple add or remove calls
43395      */
43396     beginUpdate : function(){
43397         this.updating = true;    
43398     },
43399     
43400     /**
43401      * Restore auto-layouts and optionally disable the manager from performing a layout
43402      * @param {Boolean} noLayout true to disable a layout update 
43403      */
43404     endUpdate : function(noLayout){
43405         this.updating = false;
43406         if(!noLayout){
43407             this.layout();
43408         }    
43409     },
43410     
43411     layout: function(){
43412         
43413     },
43414     
43415     onRegionResized : function(region, newSize){
43416         this.fireEvent("regionresized", region, newSize);
43417         this.layout();
43418     },
43419     
43420     onRegionCollapsed : function(region){
43421         this.fireEvent("regioncollapsed", region);
43422     },
43423     
43424     onRegionExpanded : function(region){
43425         this.fireEvent("regionexpanded", region);
43426     },
43427         
43428     /**
43429      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43430      * performs box-model adjustments.
43431      * @return {Object} The size as an object {width: (the width), height: (the height)}
43432      */
43433     getViewSize : function(){
43434         var size;
43435         if(this.el.dom != document.body){
43436             size = this.el.getSize();
43437         }else{
43438             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43439         }
43440         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43441         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43442         return size;
43443     },
43444     
43445     /**
43446      * Returns the Element this layout is bound to.
43447      * @return {Roo.Element}
43448      */
43449     getEl : function(){
43450         return this.el;
43451     },
43452     
43453     /**
43454      * Returns the specified region.
43455      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43456      * @return {Roo.LayoutRegion}
43457      */
43458     getRegion : function(target){
43459         return this.regions[target.toLowerCase()];
43460     },
43461     
43462     onWindowResize : function(){
43463         if(this.monitorWindowResize){
43464             this.layout();
43465         }
43466     }
43467 });/*
43468  * Based on:
43469  * Ext JS Library 1.1.1
43470  * Copyright(c) 2006-2007, Ext JS, LLC.
43471  *
43472  * Originally Released Under LGPL - original licence link has changed is not relivant.
43473  *
43474  * Fork - LGPL
43475  * <script type="text/javascript">
43476  */
43477 /**
43478  * @class Roo.BorderLayout
43479  * @extends Roo.LayoutManager
43480  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43481  * please see: <br><br>
43482  * <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>
43483  * <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>
43484  * Example:
43485  <pre><code>
43486  var layout = new Roo.BorderLayout(document.body, {
43487     north: {
43488         initialSize: 25,
43489         titlebar: false
43490     },
43491     west: {
43492         split:true,
43493         initialSize: 200,
43494         minSize: 175,
43495         maxSize: 400,
43496         titlebar: true,
43497         collapsible: true
43498     },
43499     east: {
43500         split:true,
43501         initialSize: 202,
43502         minSize: 175,
43503         maxSize: 400,
43504         titlebar: true,
43505         collapsible: true
43506     },
43507     south: {
43508         split:true,
43509         initialSize: 100,
43510         minSize: 100,
43511         maxSize: 200,
43512         titlebar: true,
43513         collapsible: true
43514     },
43515     center: {
43516         titlebar: true,
43517         autoScroll:true,
43518         resizeTabs: true,
43519         minTabWidth: 50,
43520         preferredTabWidth: 150
43521     }
43522 });
43523
43524 // shorthand
43525 var CP = Roo.ContentPanel;
43526
43527 layout.beginUpdate();
43528 layout.add("north", new CP("north", "North"));
43529 layout.add("south", new CP("south", {title: "South", closable: true}));
43530 layout.add("west", new CP("west", {title: "West"}));
43531 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43532 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43533 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43534 layout.getRegion("center").showPanel("center1");
43535 layout.endUpdate();
43536 </code></pre>
43537
43538 <b>The container the layout is rendered into can be either the body element or any other element.
43539 If it is not the body element, the container needs to either be an absolute positioned element,
43540 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43541 the container size if it is not the body element.</b>
43542
43543 * @constructor
43544 * Create a new BorderLayout
43545 * @param {String/HTMLElement/Element} container The container this layout is bound to
43546 * @param {Object} config Configuration options
43547  */
43548 Roo.BorderLayout = function(container, config){
43549     config = config || {};
43550     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43551     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43552     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43553         var target = this.factory.validRegions[i];
43554         if(config[target]){
43555             this.addRegion(target, config[target]);
43556         }
43557     }
43558 };
43559
43560 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43561     /**
43562      * Creates and adds a new region if it doesn't already exist.
43563      * @param {String} target The target region key (north, south, east, west or center).
43564      * @param {Object} config The regions config object
43565      * @return {BorderLayoutRegion} The new region
43566      */
43567     addRegion : function(target, config){
43568         if(!this.regions[target]){
43569             var r = this.factory.create(target, this, config);
43570             this.bindRegion(target, r);
43571         }
43572         return this.regions[target];
43573     },
43574
43575     // private (kinda)
43576     bindRegion : function(name, r){
43577         this.regions[name] = r;
43578         r.on("visibilitychange", this.layout, this);
43579         r.on("paneladded", this.layout, this);
43580         r.on("panelremoved", this.layout, this);
43581         r.on("invalidated", this.layout, this);
43582         r.on("resized", this.onRegionResized, this);
43583         r.on("collapsed", this.onRegionCollapsed, this);
43584         r.on("expanded", this.onRegionExpanded, this);
43585     },
43586
43587     /**
43588      * Performs a layout update.
43589      */
43590     layout : function(){
43591         if(this.updating) return;
43592         var size = this.getViewSize();
43593         var w = size.width;
43594         var h = size.height;
43595         var centerW = w;
43596         var centerH = h;
43597         var centerY = 0;
43598         var centerX = 0;
43599         //var x = 0, y = 0;
43600
43601         var rs = this.regions;
43602         var north = rs["north"];
43603         var south = rs["south"]; 
43604         var west = rs["west"];
43605         var east = rs["east"];
43606         var center = rs["center"];
43607         //if(this.hideOnLayout){ // not supported anymore
43608             //c.el.setStyle("display", "none");
43609         //}
43610         if(north && north.isVisible()){
43611             var b = north.getBox();
43612             var m = north.getMargins();
43613             b.width = w - (m.left+m.right);
43614             b.x = m.left;
43615             b.y = m.top;
43616             centerY = b.height + b.y + m.bottom;
43617             centerH -= centerY;
43618             north.updateBox(this.safeBox(b));
43619         }
43620         if(south && south.isVisible()){
43621             var b = south.getBox();
43622             var m = south.getMargins();
43623             b.width = w - (m.left+m.right);
43624             b.x = m.left;
43625             var totalHeight = (b.height + m.top + m.bottom);
43626             b.y = h - totalHeight + m.top;
43627             centerH -= totalHeight;
43628             south.updateBox(this.safeBox(b));
43629         }
43630         if(west && west.isVisible()){
43631             var b = west.getBox();
43632             var m = west.getMargins();
43633             b.height = centerH - (m.top+m.bottom);
43634             b.x = m.left;
43635             b.y = centerY + m.top;
43636             var totalWidth = (b.width + m.left + m.right);
43637             centerX += totalWidth;
43638             centerW -= totalWidth;
43639             west.updateBox(this.safeBox(b));
43640         }
43641         if(east && east.isVisible()){
43642             var b = east.getBox();
43643             var m = east.getMargins();
43644             b.height = centerH - (m.top+m.bottom);
43645             var totalWidth = (b.width + m.left + m.right);
43646             b.x = w - totalWidth + m.left;
43647             b.y = centerY + m.top;
43648             centerW -= totalWidth;
43649             east.updateBox(this.safeBox(b));
43650         }
43651         if(center){
43652             var m = center.getMargins();
43653             var centerBox = {
43654                 x: centerX + m.left,
43655                 y: centerY + m.top,
43656                 width: centerW - (m.left+m.right),
43657                 height: centerH - (m.top+m.bottom)
43658             };
43659             //if(this.hideOnLayout){
43660                 //center.el.setStyle("display", "block");
43661             //}
43662             center.updateBox(this.safeBox(centerBox));
43663         }
43664         this.el.repaint();
43665         this.fireEvent("layout", this);
43666     },
43667
43668     // private
43669     safeBox : function(box){
43670         box.width = Math.max(0, box.width);
43671         box.height = Math.max(0, box.height);
43672         return box;
43673     },
43674
43675     /**
43676      * Adds a ContentPanel (or subclass) to this layout.
43677      * @param {String} target The target region key (north, south, east, west or center).
43678      * @param {Roo.ContentPanel} panel The panel to add
43679      * @return {Roo.ContentPanel} The added panel
43680      */
43681     add : function(target, panel){
43682          
43683         target = target.toLowerCase();
43684         return this.regions[target].add(panel);
43685     },
43686
43687     /**
43688      * Remove a ContentPanel (or subclass) to this layout.
43689      * @param {String} target The target region key (north, south, east, west or center).
43690      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43691      * @return {Roo.ContentPanel} The removed panel
43692      */
43693     remove : function(target, panel){
43694         target = target.toLowerCase();
43695         return this.regions[target].remove(panel);
43696     },
43697
43698     /**
43699      * Searches all regions for a panel with the specified id
43700      * @param {String} panelId
43701      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43702      */
43703     findPanel : function(panelId){
43704         var rs = this.regions;
43705         for(var target in rs){
43706             if(typeof rs[target] != "function"){
43707                 var p = rs[target].getPanel(panelId);
43708                 if(p){
43709                     return p;
43710                 }
43711             }
43712         }
43713         return null;
43714     },
43715
43716     /**
43717      * Searches all regions for a panel with the specified id and activates (shows) it.
43718      * @param {String/ContentPanel} panelId The panels id or the panel itself
43719      * @return {Roo.ContentPanel} The shown panel or null
43720      */
43721     showPanel : function(panelId) {
43722       var rs = this.regions;
43723       for(var target in rs){
43724          var r = rs[target];
43725          if(typeof r != "function"){
43726             if(r.hasPanel(panelId)){
43727                return r.showPanel(panelId);
43728             }
43729          }
43730       }
43731       return null;
43732    },
43733
43734    /**
43735      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43736      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43737      */
43738     restoreState : function(provider){
43739         if(!provider){
43740             provider = Roo.state.Manager;
43741         }
43742         var sm = new Roo.LayoutStateManager();
43743         sm.init(this, provider);
43744     },
43745
43746     /**
43747      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43748      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43749      * a valid ContentPanel config object.  Example:
43750      * <pre><code>
43751 // Create the main layout
43752 var layout = new Roo.BorderLayout('main-ct', {
43753     west: {
43754         split:true,
43755         minSize: 175,
43756         titlebar: true
43757     },
43758     center: {
43759         title:'Components'
43760     }
43761 }, 'main-ct');
43762
43763 // Create and add multiple ContentPanels at once via configs
43764 layout.batchAdd({
43765    west: {
43766        id: 'source-files',
43767        autoCreate:true,
43768        title:'Ext Source Files',
43769        autoScroll:true,
43770        fitToFrame:true
43771    },
43772    center : {
43773        el: cview,
43774        autoScroll:true,
43775        fitToFrame:true,
43776        toolbar: tb,
43777        resizeEl:'cbody'
43778    }
43779 });
43780 </code></pre>
43781      * @param {Object} regions An object containing ContentPanel configs by region name
43782      */
43783     batchAdd : function(regions){
43784         this.beginUpdate();
43785         for(var rname in regions){
43786             var lr = this.regions[rname];
43787             if(lr){
43788                 this.addTypedPanels(lr, regions[rname]);
43789             }
43790         }
43791         this.endUpdate();
43792     },
43793
43794     // private
43795     addTypedPanels : function(lr, ps){
43796         if(typeof ps == 'string'){
43797             lr.add(new Roo.ContentPanel(ps));
43798         }
43799         else if(ps instanceof Array){
43800             for(var i =0, len = ps.length; i < len; i++){
43801                 this.addTypedPanels(lr, ps[i]);
43802             }
43803         }
43804         else if(!ps.events){ // raw config?
43805             var el = ps.el;
43806             delete ps.el; // prevent conflict
43807             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43808         }
43809         else {  // panel object assumed!
43810             lr.add(ps);
43811         }
43812     },
43813     /**
43814      * Adds a xtype elements to the layout.
43815      * <pre><code>
43816
43817 layout.addxtype({
43818        xtype : 'ContentPanel',
43819        region: 'west',
43820        items: [ .... ]
43821    }
43822 );
43823
43824 layout.addxtype({
43825         xtype : 'NestedLayoutPanel',
43826         region: 'west',
43827         layout: {
43828            center: { },
43829            west: { }   
43830         },
43831         items : [ ... list of content panels or nested layout panels.. ]
43832    }
43833 );
43834 </code></pre>
43835      * @param {Object} cfg Xtype definition of item to add.
43836      */
43837     addxtype : function(cfg)
43838     {
43839         // basically accepts a pannel...
43840         // can accept a layout region..!?!?
43841        // console.log('BorderLayout add ' + cfg.xtype)
43842         
43843         if (!cfg.xtype.match(/Panel$/)) {
43844             return false;
43845         }
43846         var ret = false;
43847         var region = cfg.region;
43848         delete cfg.region;
43849         
43850           
43851         var xitems = [];
43852         if (cfg.items) {
43853             xitems = cfg.items;
43854             delete cfg.items;
43855         }
43856         
43857         
43858         switch(cfg.xtype) 
43859         {
43860             case 'ContentPanel':  // ContentPanel (el, cfg)
43861             case 'ScrollPanel':  // ContentPanel (el, cfg)
43862                 if(cfg.autoCreate) {
43863                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43864                 } else {
43865                     var el = this.el.createChild();
43866                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43867                 }
43868                 
43869                 this.add(region, ret);
43870                 break;
43871             
43872             
43873             case 'TreePanel': // our new panel!
43874                 cfg.el = this.el.createChild();
43875                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43876                 this.add(region, ret);
43877                 break;
43878             
43879             case 'NestedLayoutPanel': 
43880                 // create a new Layout (which is  a Border Layout...
43881                 var el = this.el.createChild();
43882                 var clayout = cfg.layout;
43883                 delete cfg.layout;
43884                 clayout.items   = clayout.items  || [];
43885                 // replace this exitems with the clayout ones..
43886                 xitems = clayout.items;
43887                  
43888                 
43889                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43890                     cfg.background = false;
43891                 }
43892                 var layout = new Roo.BorderLayout(el, clayout);
43893                 
43894                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43895                 //console.log('adding nested layout panel '  + cfg.toSource());
43896                 this.add(region, ret);
43897                 
43898                 break;
43899                 
43900             case 'GridPanel': 
43901             
43902                 // needs grid and region
43903                 
43904                 //var el = this.getRegion(region).el.createChild();
43905                 var el = this.el.createChild();
43906                 // create the grid first...
43907                 
43908                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43909                 delete cfg.grid;
43910                 if (region == 'center' && this.active ) {
43911                     cfg.background = false;
43912                 }
43913                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43914                 
43915                 this.add(region, ret);
43916                 if (cfg.background) {
43917                     ret.on('activate', function(gp) {
43918                         if (!gp.grid.rendered) {
43919                             gp.grid.render();
43920                         }
43921                     });
43922                 } else {
43923                     grid.render();
43924                 }
43925                 break;
43926            
43927                
43928                 
43929                 
43930             default: 
43931                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43932                 return;
43933              // GridPanel (grid, cfg)
43934             
43935         }
43936         this.beginUpdate();
43937         // add children..
43938         Roo.each(xitems, function(i)  {
43939             ret.addxtype(i);
43940         });
43941         this.endUpdate();
43942         return ret;
43943         
43944     }
43945 });
43946
43947 /**
43948  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43949  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43950  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43951  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43952  * <pre><code>
43953 // shorthand
43954 var CP = Roo.ContentPanel;
43955
43956 var layout = Roo.BorderLayout.create({
43957     north: {
43958         initialSize: 25,
43959         titlebar: false,
43960         panels: [new CP("north", "North")]
43961     },
43962     west: {
43963         split:true,
43964         initialSize: 200,
43965         minSize: 175,
43966         maxSize: 400,
43967         titlebar: true,
43968         collapsible: true,
43969         panels: [new CP("west", {title: "West"})]
43970     },
43971     east: {
43972         split:true,
43973         initialSize: 202,
43974         minSize: 175,
43975         maxSize: 400,
43976         titlebar: true,
43977         collapsible: true,
43978         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43979     },
43980     south: {
43981         split:true,
43982         initialSize: 100,
43983         minSize: 100,
43984         maxSize: 200,
43985         titlebar: true,
43986         collapsible: true,
43987         panels: [new CP("south", {title: "South", closable: true})]
43988     },
43989     center: {
43990         titlebar: true,
43991         autoScroll:true,
43992         resizeTabs: true,
43993         minTabWidth: 50,
43994         preferredTabWidth: 150,
43995         panels: [
43996             new CP("center1", {title: "Close Me", closable: true}),
43997             new CP("center2", {title: "Center Panel", closable: false})
43998         ]
43999     }
44000 }, document.body);
44001
44002 layout.getRegion("center").showPanel("center1");
44003 </code></pre>
44004  * @param config
44005  * @param targetEl
44006  */
44007 Roo.BorderLayout.create = function(config, targetEl){
44008     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44009     layout.beginUpdate();
44010     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44011     for(var j = 0, jlen = regions.length; j < jlen; j++){
44012         var lr = regions[j];
44013         if(layout.regions[lr] && config[lr].panels){
44014             var r = layout.regions[lr];
44015             var ps = config[lr].panels;
44016             layout.addTypedPanels(r, ps);
44017         }
44018     }
44019     layout.endUpdate();
44020     return layout;
44021 };
44022
44023 // private
44024 Roo.BorderLayout.RegionFactory = {
44025     // private
44026     validRegions : ["north","south","east","west","center"],
44027
44028     // private
44029     create : function(target, mgr, config){
44030         target = target.toLowerCase();
44031         if(config.lightweight || config.basic){
44032             return new Roo.BasicLayoutRegion(mgr, config, target);
44033         }
44034         switch(target){
44035             case "north":
44036                 return new Roo.NorthLayoutRegion(mgr, config);
44037             case "south":
44038                 return new Roo.SouthLayoutRegion(mgr, config);
44039             case "east":
44040                 return new Roo.EastLayoutRegion(mgr, config);
44041             case "west":
44042                 return new Roo.WestLayoutRegion(mgr, config);
44043             case "center":
44044                 return new Roo.CenterLayoutRegion(mgr, config);
44045         }
44046         throw 'Layout region "'+target+'" not supported.';
44047     }
44048 };/*
44049  * Based on:
44050  * Ext JS Library 1.1.1
44051  * Copyright(c) 2006-2007, Ext JS, LLC.
44052  *
44053  * Originally Released Under LGPL - original licence link has changed is not relivant.
44054  *
44055  * Fork - LGPL
44056  * <script type="text/javascript">
44057  */
44058  
44059 /**
44060  * @class Roo.BasicLayoutRegion
44061  * @extends Roo.util.Observable
44062  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44063  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44064  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44065  */
44066 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44067     this.mgr = mgr;
44068     this.position  = pos;
44069     this.events = {
44070         /**
44071          * @scope Roo.BasicLayoutRegion
44072          */
44073         
44074         /**
44075          * @event beforeremove
44076          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44077          * @param {Roo.LayoutRegion} this
44078          * @param {Roo.ContentPanel} panel The panel
44079          * @param {Object} e The cancel event object
44080          */
44081         "beforeremove" : true,
44082         /**
44083          * @event invalidated
44084          * Fires when the layout for this region is changed.
44085          * @param {Roo.LayoutRegion} this
44086          */
44087         "invalidated" : true,
44088         /**
44089          * @event visibilitychange
44090          * Fires when this region is shown or hidden 
44091          * @param {Roo.LayoutRegion} this
44092          * @param {Boolean} visibility true or false
44093          */
44094         "visibilitychange" : true,
44095         /**
44096          * @event paneladded
44097          * Fires when a panel is added. 
44098          * @param {Roo.LayoutRegion} this
44099          * @param {Roo.ContentPanel} panel The panel
44100          */
44101         "paneladded" : true,
44102         /**
44103          * @event panelremoved
44104          * Fires when a panel is removed. 
44105          * @param {Roo.LayoutRegion} this
44106          * @param {Roo.ContentPanel} panel The panel
44107          */
44108         "panelremoved" : true,
44109         /**
44110          * @event collapsed
44111          * Fires when this region is collapsed.
44112          * @param {Roo.LayoutRegion} this
44113          */
44114         "collapsed" : true,
44115         /**
44116          * @event expanded
44117          * Fires when this region is expanded.
44118          * @param {Roo.LayoutRegion} this
44119          */
44120         "expanded" : true,
44121         /**
44122          * @event slideshow
44123          * Fires when this region is slid into view.
44124          * @param {Roo.LayoutRegion} this
44125          */
44126         "slideshow" : true,
44127         /**
44128          * @event slidehide
44129          * Fires when this region slides out of view. 
44130          * @param {Roo.LayoutRegion} this
44131          */
44132         "slidehide" : true,
44133         /**
44134          * @event panelactivated
44135          * Fires when a panel is activated. 
44136          * @param {Roo.LayoutRegion} this
44137          * @param {Roo.ContentPanel} panel The activated panel
44138          */
44139         "panelactivated" : true,
44140         /**
44141          * @event resized
44142          * Fires when the user resizes this region. 
44143          * @param {Roo.LayoutRegion} this
44144          * @param {Number} newSize The new size (width for east/west, height for north/south)
44145          */
44146         "resized" : true
44147     };
44148     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44149     this.panels = new Roo.util.MixedCollection();
44150     this.panels.getKey = this.getPanelId.createDelegate(this);
44151     this.box = null;
44152     this.activePanel = null;
44153     // ensure listeners are added...
44154     
44155     if (config.listeners || config.events) {
44156         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44157             listeners : config.listeners || {},
44158             events : config.events || {}
44159         });
44160     }
44161     
44162     if(skipConfig !== true){
44163         this.applyConfig(config);
44164     }
44165 };
44166
44167 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44168     getPanelId : function(p){
44169         return p.getId();
44170     },
44171     
44172     applyConfig : function(config){
44173         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44174         this.config = config;
44175         
44176     },
44177     
44178     /**
44179      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44180      * the width, for horizontal (north, south) the height.
44181      * @param {Number} newSize The new width or height
44182      */
44183     resizeTo : function(newSize){
44184         var el = this.el ? this.el :
44185                  (this.activePanel ? this.activePanel.getEl() : null);
44186         if(el){
44187             switch(this.position){
44188                 case "east":
44189                 case "west":
44190                     el.setWidth(newSize);
44191                     this.fireEvent("resized", this, newSize);
44192                 break;
44193                 case "north":
44194                 case "south":
44195                     el.setHeight(newSize);
44196                     this.fireEvent("resized", this, newSize);
44197                 break;                
44198             }
44199         }
44200     },
44201     
44202     getBox : function(){
44203         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44204     },
44205     
44206     getMargins : function(){
44207         return this.margins;
44208     },
44209     
44210     updateBox : function(box){
44211         this.box = box;
44212         var el = this.activePanel.getEl();
44213         el.dom.style.left = box.x + "px";
44214         el.dom.style.top = box.y + "px";
44215         this.activePanel.setSize(box.width, box.height);
44216     },
44217     
44218     /**
44219      * Returns the container element for this region.
44220      * @return {Roo.Element}
44221      */
44222     getEl : function(){
44223         return this.activePanel;
44224     },
44225     
44226     /**
44227      * Returns true if this region is currently visible.
44228      * @return {Boolean}
44229      */
44230     isVisible : function(){
44231         return this.activePanel ? true : false;
44232     },
44233     
44234     setActivePanel : function(panel){
44235         panel = this.getPanel(panel);
44236         if(this.activePanel && this.activePanel != panel){
44237             this.activePanel.setActiveState(false);
44238             this.activePanel.getEl().setLeftTop(-10000,-10000);
44239         }
44240         this.activePanel = panel;
44241         panel.setActiveState(true);
44242         if(this.box){
44243             panel.setSize(this.box.width, this.box.height);
44244         }
44245         this.fireEvent("panelactivated", this, panel);
44246         this.fireEvent("invalidated");
44247     },
44248     
44249     /**
44250      * Show the specified panel.
44251      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44252      * @return {Roo.ContentPanel} The shown panel or null
44253      */
44254     showPanel : function(panel){
44255         if(panel = this.getPanel(panel)){
44256             this.setActivePanel(panel);
44257         }
44258         return panel;
44259     },
44260     
44261     /**
44262      * Get the active panel for this region.
44263      * @return {Roo.ContentPanel} The active panel or null
44264      */
44265     getActivePanel : function(){
44266         return this.activePanel;
44267     },
44268     
44269     /**
44270      * Add the passed ContentPanel(s)
44271      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44272      * @return {Roo.ContentPanel} The panel added (if only one was added)
44273      */
44274     add : function(panel){
44275         if(arguments.length > 1){
44276             for(var i = 0, len = arguments.length; i < len; i++) {
44277                 this.add(arguments[i]);
44278             }
44279             return null;
44280         }
44281         if(this.hasPanel(panel)){
44282             this.showPanel(panel);
44283             return panel;
44284         }
44285         var el = panel.getEl();
44286         if(el.dom.parentNode != this.mgr.el.dom){
44287             this.mgr.el.dom.appendChild(el.dom);
44288         }
44289         if(panel.setRegion){
44290             panel.setRegion(this);
44291         }
44292         this.panels.add(panel);
44293         el.setStyle("position", "absolute");
44294         if(!panel.background){
44295             this.setActivePanel(panel);
44296             if(this.config.initialSize && this.panels.getCount()==1){
44297                 this.resizeTo(this.config.initialSize);
44298             }
44299         }
44300         this.fireEvent("paneladded", this, panel);
44301         return panel;
44302     },
44303     
44304     /**
44305      * Returns true if the panel is in this region.
44306      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44307      * @return {Boolean}
44308      */
44309     hasPanel : function(panel){
44310         if(typeof panel == "object"){ // must be panel obj
44311             panel = panel.getId();
44312         }
44313         return this.getPanel(panel) ? true : false;
44314     },
44315     
44316     /**
44317      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44318      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44319      * @param {Boolean} preservePanel Overrides the config preservePanel option
44320      * @return {Roo.ContentPanel} The panel that was removed
44321      */
44322     remove : function(panel, preservePanel){
44323         panel = this.getPanel(panel);
44324         if(!panel){
44325             return null;
44326         }
44327         var e = {};
44328         this.fireEvent("beforeremove", this, panel, e);
44329         if(e.cancel === true){
44330             return null;
44331         }
44332         var panelId = panel.getId();
44333         this.panels.removeKey(panelId);
44334         return panel;
44335     },
44336     
44337     /**
44338      * Returns the panel specified or null if it's not in this region.
44339      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44340      * @return {Roo.ContentPanel}
44341      */
44342     getPanel : function(id){
44343         if(typeof id == "object"){ // must be panel obj
44344             return id;
44345         }
44346         return this.panels.get(id);
44347     },
44348     
44349     /**
44350      * Returns this regions position (north/south/east/west/center).
44351      * @return {String} 
44352      */
44353     getPosition: function(){
44354         return this.position;    
44355     }
44356 });/*
44357  * Based on:
44358  * Ext JS Library 1.1.1
44359  * Copyright(c) 2006-2007, Ext JS, LLC.
44360  *
44361  * Originally Released Under LGPL - original licence link has changed is not relivant.
44362  *
44363  * Fork - LGPL
44364  * <script type="text/javascript">
44365  */
44366  
44367 /**
44368  * @class Roo.LayoutRegion
44369  * @extends Roo.BasicLayoutRegion
44370  * This class represents a region in a layout manager.
44371  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44372  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44373  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44374  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44375  * @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})
44376  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44377  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44378  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44379  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44380  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44381  * @cfg {String} title The title for the region (overrides panel titles)
44382  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44383  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44384  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44385  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44386  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44387  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44388  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44389  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44390  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44391  * @cfg {Boolean} showPin True to show a pin button
44392 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44393 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44394 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44395 * @cfg {Number} width  For East/West panels
44396 * @cfg {Number} height For North/South panels
44397 * @cfg {Boolean} split To show the splitter
44398  */
44399 Roo.LayoutRegion = function(mgr, config, pos){
44400     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44401     var dh = Roo.DomHelper;
44402     /** This region's container element 
44403     * @type Roo.Element */
44404     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44405     /** This region's title element 
44406     * @type Roo.Element */
44407
44408     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44409         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44410         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44411     ]}, true);
44412     this.titleEl.enableDisplayMode();
44413     /** This region's title text element 
44414     * @type HTMLElement */
44415     this.titleTextEl = this.titleEl.dom.firstChild;
44416     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44417     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44418     this.closeBtn.enableDisplayMode();
44419     this.closeBtn.on("click", this.closeClicked, this);
44420     this.closeBtn.hide();
44421
44422     this.createBody(config);
44423     this.visible = true;
44424     this.collapsed = false;
44425
44426     if(config.hideWhenEmpty){
44427         this.hide();
44428         this.on("paneladded", this.validateVisibility, this);
44429         this.on("panelremoved", this.validateVisibility, this);
44430     }
44431     this.applyConfig(config);
44432 };
44433
44434 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44435
44436     createBody : function(){
44437         /** This region's body element 
44438         * @type Roo.Element */
44439         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44440     },
44441
44442     applyConfig : function(c){
44443         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44444             var dh = Roo.DomHelper;
44445             if(c.titlebar !== false){
44446                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44447                 this.collapseBtn.on("click", this.collapse, this);
44448                 this.collapseBtn.enableDisplayMode();
44449
44450                 if(c.showPin === true || this.showPin){
44451                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44452                     this.stickBtn.enableDisplayMode();
44453                     this.stickBtn.on("click", this.expand, this);
44454                     this.stickBtn.hide();
44455                 }
44456             }
44457             /** This region's collapsed element
44458             * @type Roo.Element */
44459             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44460                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44461             ]}, true);
44462             if(c.floatable !== false){
44463                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44464                this.collapsedEl.on("click", this.collapseClick, this);
44465             }
44466
44467             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44468                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44469                    id: "message", unselectable: "on", style:{"float":"left"}});
44470                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44471              }
44472             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44473             this.expandBtn.on("click", this.expand, this);
44474         }
44475         if(this.collapseBtn){
44476             this.collapseBtn.setVisible(c.collapsible == true);
44477         }
44478         this.cmargins = c.cmargins || this.cmargins ||
44479                          (this.position == "west" || this.position == "east" ?
44480                              {top: 0, left: 2, right:2, bottom: 0} :
44481                              {top: 2, left: 0, right:0, bottom: 2});
44482         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44483         this.bottomTabs = c.tabPosition != "top";
44484         this.autoScroll = c.autoScroll || false;
44485         if(this.autoScroll){
44486             this.bodyEl.setStyle("overflow", "auto");
44487         }else{
44488             this.bodyEl.setStyle("overflow", "hidden");
44489         }
44490         //if(c.titlebar !== false){
44491             if((!c.titlebar && !c.title) || c.titlebar === false){
44492                 this.titleEl.hide();
44493             }else{
44494                 this.titleEl.show();
44495                 if(c.title){
44496                     this.titleTextEl.innerHTML = c.title;
44497                 }
44498             }
44499         //}
44500         this.duration = c.duration || .30;
44501         this.slideDuration = c.slideDuration || .45;
44502         this.config = c;
44503         if(c.collapsed){
44504             this.collapse(true);
44505         }
44506         if(c.hidden){
44507             this.hide();
44508         }
44509     },
44510     /**
44511      * Returns true if this region is currently visible.
44512      * @return {Boolean}
44513      */
44514     isVisible : function(){
44515         return this.visible;
44516     },
44517
44518     /**
44519      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44520      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44521      */
44522     setCollapsedTitle : function(title){
44523         title = title || "&#160;";
44524         if(this.collapsedTitleTextEl){
44525             this.collapsedTitleTextEl.innerHTML = title;
44526         }
44527     },
44528
44529     getBox : function(){
44530         var b;
44531         if(!this.collapsed){
44532             b = this.el.getBox(false, true);
44533         }else{
44534             b = this.collapsedEl.getBox(false, true);
44535         }
44536         return b;
44537     },
44538
44539     getMargins : function(){
44540         return this.collapsed ? this.cmargins : this.margins;
44541     },
44542
44543     highlight : function(){
44544         this.el.addClass("x-layout-panel-dragover");
44545     },
44546
44547     unhighlight : function(){
44548         this.el.removeClass("x-layout-panel-dragover");
44549     },
44550
44551     updateBox : function(box){
44552         this.box = box;
44553         if(!this.collapsed){
44554             this.el.dom.style.left = box.x + "px";
44555             this.el.dom.style.top = box.y + "px";
44556             this.updateBody(box.width, box.height);
44557         }else{
44558             this.collapsedEl.dom.style.left = box.x + "px";
44559             this.collapsedEl.dom.style.top = box.y + "px";
44560             this.collapsedEl.setSize(box.width, box.height);
44561         }
44562         if(this.tabs){
44563             this.tabs.autoSizeTabs();
44564         }
44565     },
44566
44567     updateBody : function(w, h){
44568         if(w !== null){
44569             this.el.setWidth(w);
44570             w -= this.el.getBorderWidth("rl");
44571             if(this.config.adjustments){
44572                 w += this.config.adjustments[0];
44573             }
44574         }
44575         if(h !== null){
44576             this.el.setHeight(h);
44577             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44578             h -= this.el.getBorderWidth("tb");
44579             if(this.config.adjustments){
44580                 h += this.config.adjustments[1];
44581             }
44582             this.bodyEl.setHeight(h);
44583             if(this.tabs){
44584                 h = this.tabs.syncHeight(h);
44585             }
44586         }
44587         if(this.panelSize){
44588             w = w !== null ? w : this.panelSize.width;
44589             h = h !== null ? h : this.panelSize.height;
44590         }
44591         if(this.activePanel){
44592             var el = this.activePanel.getEl();
44593             w = w !== null ? w : el.getWidth();
44594             h = h !== null ? h : el.getHeight();
44595             this.panelSize = {width: w, height: h};
44596             this.activePanel.setSize(w, h);
44597         }
44598         if(Roo.isIE && this.tabs){
44599             this.tabs.el.repaint();
44600         }
44601     },
44602
44603     /**
44604      * Returns the container element for this region.
44605      * @return {Roo.Element}
44606      */
44607     getEl : function(){
44608         return this.el;
44609     },
44610
44611     /**
44612      * Hides this region.
44613      */
44614     hide : function(){
44615         if(!this.collapsed){
44616             this.el.dom.style.left = "-2000px";
44617             this.el.hide();
44618         }else{
44619             this.collapsedEl.dom.style.left = "-2000px";
44620             this.collapsedEl.hide();
44621         }
44622         this.visible = false;
44623         this.fireEvent("visibilitychange", this, false);
44624     },
44625
44626     /**
44627      * Shows this region if it was previously hidden.
44628      */
44629     show : function(){
44630         if(!this.collapsed){
44631             this.el.show();
44632         }else{
44633             this.collapsedEl.show();
44634         }
44635         this.visible = true;
44636         this.fireEvent("visibilitychange", this, true);
44637     },
44638
44639     closeClicked : function(){
44640         if(this.activePanel){
44641             this.remove(this.activePanel);
44642         }
44643     },
44644
44645     collapseClick : function(e){
44646         if(this.isSlid){
44647            e.stopPropagation();
44648            this.slideIn();
44649         }else{
44650            e.stopPropagation();
44651            this.slideOut();
44652         }
44653     },
44654
44655     /**
44656      * Collapses this region.
44657      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44658      */
44659     collapse : function(skipAnim){
44660         if(this.collapsed) return;
44661         this.collapsed = true;
44662         if(this.split){
44663             this.split.el.hide();
44664         }
44665         if(this.config.animate && skipAnim !== true){
44666             this.fireEvent("invalidated", this);
44667             this.animateCollapse();
44668         }else{
44669             this.el.setLocation(-20000,-20000);
44670             this.el.hide();
44671             this.collapsedEl.show();
44672             this.fireEvent("collapsed", this);
44673             this.fireEvent("invalidated", this);
44674         }
44675     },
44676
44677     animateCollapse : function(){
44678         // overridden
44679     },
44680
44681     /**
44682      * Expands this region if it was previously collapsed.
44683      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44684      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44685      */
44686     expand : function(e, skipAnim){
44687         if(e) e.stopPropagation();
44688         if(!this.collapsed || this.el.hasActiveFx()) return;
44689         if(this.isSlid){
44690             this.afterSlideIn();
44691             skipAnim = true;
44692         }
44693         this.collapsed = false;
44694         if(this.config.animate && skipAnim !== true){
44695             this.animateExpand();
44696         }else{
44697             this.el.show();
44698             if(this.split){
44699                 this.split.el.show();
44700             }
44701             this.collapsedEl.setLocation(-2000,-2000);
44702             this.collapsedEl.hide();
44703             this.fireEvent("invalidated", this);
44704             this.fireEvent("expanded", this);
44705         }
44706     },
44707
44708     animateExpand : function(){
44709         // overridden
44710     },
44711
44712     initTabs : function(){
44713         this.bodyEl.setStyle("overflow", "hidden");
44714         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44715             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44716             disableTooltips: this.config.disableTabTips
44717         });
44718         if(this.config.hideTabs){
44719             ts.stripWrap.setDisplayed(false);
44720         }
44721         this.tabs = ts;
44722         ts.resizeTabs = this.config.resizeTabs === true;
44723         ts.minTabWidth = this.config.minTabWidth || 40;
44724         ts.maxTabWidth = this.config.maxTabWidth || 250;
44725         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44726         ts.monitorResize = false;
44727         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44728         ts.bodyEl.addClass('x-layout-tabs-body');
44729         this.panels.each(this.initPanelAsTab, this);
44730     },
44731
44732     initPanelAsTab : function(panel){
44733         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44734                     this.config.closeOnTab && panel.isClosable());
44735         if(panel.tabTip !== undefined){
44736             ti.setTooltip(panel.tabTip);
44737         }
44738         ti.on("activate", function(){
44739               this.setActivePanel(panel);
44740         }, this);
44741         if(this.config.closeOnTab){
44742             ti.on("beforeclose", function(t, e){
44743                 e.cancel = true;
44744                 this.remove(panel);
44745             }, this);
44746         }
44747         return ti;
44748     },
44749
44750     updatePanelTitle : function(panel, title){
44751         if(this.activePanel == panel){
44752             this.updateTitle(title);
44753         }
44754         if(this.tabs){
44755             var ti = this.tabs.getTab(panel.getEl().id);
44756             ti.setText(title);
44757             if(panel.tabTip !== undefined){
44758                 ti.setTooltip(panel.tabTip);
44759             }
44760         }
44761     },
44762
44763     updateTitle : function(title){
44764         if(this.titleTextEl && !this.config.title){
44765             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44766         }
44767     },
44768
44769     setActivePanel : function(panel){
44770         panel = this.getPanel(panel);
44771         if(this.activePanel && this.activePanel != panel){
44772             this.activePanel.setActiveState(false);
44773         }
44774         this.activePanel = panel;
44775         panel.setActiveState(true);
44776         if(this.panelSize){
44777             panel.setSize(this.panelSize.width, this.panelSize.height);
44778         }
44779         if(this.closeBtn){
44780             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44781         }
44782         this.updateTitle(panel.getTitle());
44783         if(this.tabs){
44784             this.fireEvent("invalidated", this);
44785         }
44786         this.fireEvent("panelactivated", this, panel);
44787     },
44788
44789     /**
44790      * Shows the specified panel.
44791      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44792      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44793      */
44794     showPanel : function(panel){
44795         if(panel = this.getPanel(panel)){
44796             if(this.tabs){
44797                 var tab = this.tabs.getTab(panel.getEl().id);
44798                 if(tab.isHidden()){
44799                     this.tabs.unhideTab(tab.id);
44800                 }
44801                 tab.activate();
44802             }else{
44803                 this.setActivePanel(panel);
44804             }
44805         }
44806         return panel;
44807     },
44808
44809     /**
44810      * Get the active panel for this region.
44811      * @return {Roo.ContentPanel} The active panel or null
44812      */
44813     getActivePanel : function(){
44814         return this.activePanel;
44815     },
44816
44817     validateVisibility : function(){
44818         if(this.panels.getCount() < 1){
44819             this.updateTitle("&#160;");
44820             this.closeBtn.hide();
44821             this.hide();
44822         }else{
44823             if(!this.isVisible()){
44824                 this.show();
44825             }
44826         }
44827     },
44828
44829     /**
44830      * Adds the passed ContentPanel(s) to this region.
44831      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44832      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44833      */
44834     add : function(panel){
44835         if(arguments.length > 1){
44836             for(var i = 0, len = arguments.length; i < len; i++) {
44837                 this.add(arguments[i]);
44838             }
44839             return null;
44840         }
44841         if(this.hasPanel(panel)){
44842             this.showPanel(panel);
44843             return panel;
44844         }
44845         panel.setRegion(this);
44846         this.panels.add(panel);
44847         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44848             this.bodyEl.dom.appendChild(panel.getEl().dom);
44849             if(panel.background !== true){
44850                 this.setActivePanel(panel);
44851             }
44852             this.fireEvent("paneladded", this, panel);
44853             return panel;
44854         }
44855         if(!this.tabs){
44856             this.initTabs();
44857         }else{
44858             this.initPanelAsTab(panel);
44859         }
44860         if(panel.background !== true){
44861             this.tabs.activate(panel.getEl().id);
44862         }
44863         this.fireEvent("paneladded", this, panel);
44864         return panel;
44865     },
44866
44867     /**
44868      * Hides the tab for the specified panel.
44869      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44870      */
44871     hidePanel : function(panel){
44872         if(this.tabs && (panel = this.getPanel(panel))){
44873             this.tabs.hideTab(panel.getEl().id);
44874         }
44875     },
44876
44877     /**
44878      * Unhides the tab for a previously hidden panel.
44879      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44880      */
44881     unhidePanel : function(panel){
44882         if(this.tabs && (panel = this.getPanel(panel))){
44883             this.tabs.unhideTab(panel.getEl().id);
44884         }
44885     },
44886
44887     clearPanels : function(){
44888         while(this.panels.getCount() > 0){
44889              this.remove(this.panels.first());
44890         }
44891     },
44892
44893     /**
44894      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44895      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44896      * @param {Boolean} preservePanel Overrides the config preservePanel option
44897      * @return {Roo.ContentPanel} The panel that was removed
44898      */
44899     remove : function(panel, preservePanel){
44900         panel = this.getPanel(panel);
44901         if(!panel){
44902             return null;
44903         }
44904         var e = {};
44905         this.fireEvent("beforeremove", this, panel, e);
44906         if(e.cancel === true){
44907             return null;
44908         }
44909         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44910         var panelId = panel.getId();
44911         this.panels.removeKey(panelId);
44912         if(preservePanel){
44913             document.body.appendChild(panel.getEl().dom);
44914         }
44915         if(this.tabs){
44916             this.tabs.removeTab(panel.getEl().id);
44917         }else if (!preservePanel){
44918             this.bodyEl.dom.removeChild(panel.getEl().dom);
44919         }
44920         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44921             var p = this.panels.first();
44922             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44923             tempEl.appendChild(p.getEl().dom);
44924             this.bodyEl.update("");
44925             this.bodyEl.dom.appendChild(p.getEl().dom);
44926             tempEl = null;
44927             this.updateTitle(p.getTitle());
44928             this.tabs = null;
44929             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44930             this.setActivePanel(p);
44931         }
44932         panel.setRegion(null);
44933         if(this.activePanel == panel){
44934             this.activePanel = null;
44935         }
44936         if(this.config.autoDestroy !== false && preservePanel !== true){
44937             try{panel.destroy();}catch(e){}
44938         }
44939         this.fireEvent("panelremoved", this, panel);
44940         return panel;
44941     },
44942
44943     /**
44944      * Returns the TabPanel component used by this region
44945      * @return {Roo.TabPanel}
44946      */
44947     getTabs : function(){
44948         return this.tabs;
44949     },
44950
44951     createTool : function(parentEl, className){
44952         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44953             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44954         btn.addClassOnOver("x-layout-tools-button-over");
44955         return btn;
44956     }
44957 });/*
44958  * Based on:
44959  * Ext JS Library 1.1.1
44960  * Copyright(c) 2006-2007, Ext JS, LLC.
44961  *
44962  * Originally Released Under LGPL - original licence link has changed is not relivant.
44963  *
44964  * Fork - LGPL
44965  * <script type="text/javascript">
44966  */
44967  
44968
44969
44970 /**
44971  * @class Roo.SplitLayoutRegion
44972  * @extends Roo.LayoutRegion
44973  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44974  */
44975 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44976     this.cursor = cursor;
44977     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44978 };
44979
44980 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44981     splitTip : "Drag to resize.",
44982     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44983     useSplitTips : false,
44984
44985     applyConfig : function(config){
44986         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44987         if(config.split){
44988             if(!this.split){
44989                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44990                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44991                 /** The SplitBar for this region 
44992                 * @type Roo.SplitBar */
44993                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44994                 this.split.on("moved", this.onSplitMove, this);
44995                 this.split.useShim = config.useShim === true;
44996                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44997                 if(this.useSplitTips){
44998                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44999                 }
45000                 if(config.collapsible){
45001                     this.split.el.on("dblclick", this.collapse,  this);
45002                 }
45003             }
45004             if(typeof config.minSize != "undefined"){
45005                 this.split.minSize = config.minSize;
45006             }
45007             if(typeof config.maxSize != "undefined"){
45008                 this.split.maxSize = config.maxSize;
45009             }
45010             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45011                 this.hideSplitter();
45012             }
45013         }
45014     },
45015
45016     getHMaxSize : function(){
45017          var cmax = this.config.maxSize || 10000;
45018          var center = this.mgr.getRegion("center");
45019          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45020     },
45021
45022     getVMaxSize : function(){
45023          var cmax = this.config.maxSize || 10000;
45024          var center = this.mgr.getRegion("center");
45025          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45026     },
45027
45028     onSplitMove : function(split, newSize){
45029         this.fireEvent("resized", this, newSize);
45030     },
45031     
45032     /** 
45033      * Returns the {@link Roo.SplitBar} for this region.
45034      * @return {Roo.SplitBar}
45035      */
45036     getSplitBar : function(){
45037         return this.split;
45038     },
45039     
45040     hide : function(){
45041         this.hideSplitter();
45042         Roo.SplitLayoutRegion.superclass.hide.call(this);
45043     },
45044
45045     hideSplitter : function(){
45046         if(this.split){
45047             this.split.el.setLocation(-2000,-2000);
45048             this.split.el.hide();
45049         }
45050     },
45051
45052     show : function(){
45053         if(this.split){
45054             this.split.el.show();
45055         }
45056         Roo.SplitLayoutRegion.superclass.show.call(this);
45057     },
45058     
45059     beforeSlide: function(){
45060         if(Roo.isGecko){// firefox overflow auto bug workaround
45061             this.bodyEl.clip();
45062             if(this.tabs) this.tabs.bodyEl.clip();
45063             if(this.activePanel){
45064                 this.activePanel.getEl().clip();
45065                 
45066                 if(this.activePanel.beforeSlide){
45067                     this.activePanel.beforeSlide();
45068                 }
45069             }
45070         }
45071     },
45072     
45073     afterSlide : function(){
45074         if(Roo.isGecko){// firefox overflow auto bug workaround
45075             this.bodyEl.unclip();
45076             if(this.tabs) this.tabs.bodyEl.unclip();
45077             if(this.activePanel){
45078                 this.activePanel.getEl().unclip();
45079                 if(this.activePanel.afterSlide){
45080                     this.activePanel.afterSlide();
45081                 }
45082             }
45083         }
45084     },
45085
45086     initAutoHide : function(){
45087         if(this.autoHide !== false){
45088             if(!this.autoHideHd){
45089                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45090                 this.autoHideHd = {
45091                     "mouseout": function(e){
45092                         if(!e.within(this.el, true)){
45093                             st.delay(500);
45094                         }
45095                     },
45096                     "mouseover" : function(e){
45097                         st.cancel();
45098                     },
45099                     scope : this
45100                 };
45101             }
45102             this.el.on(this.autoHideHd);
45103         }
45104     },
45105
45106     clearAutoHide : function(){
45107         if(this.autoHide !== false){
45108             this.el.un("mouseout", this.autoHideHd.mouseout);
45109             this.el.un("mouseover", this.autoHideHd.mouseover);
45110         }
45111     },
45112
45113     clearMonitor : function(){
45114         Roo.get(document).un("click", this.slideInIf, this);
45115     },
45116
45117     // these names are backwards but not changed for compat
45118     slideOut : function(){
45119         if(this.isSlid || this.el.hasActiveFx()){
45120             return;
45121         }
45122         this.isSlid = true;
45123         if(this.collapseBtn){
45124             this.collapseBtn.hide();
45125         }
45126         this.closeBtnState = this.closeBtn.getStyle('display');
45127         this.closeBtn.hide();
45128         if(this.stickBtn){
45129             this.stickBtn.show();
45130         }
45131         this.el.show();
45132         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45133         this.beforeSlide();
45134         this.el.setStyle("z-index", 10001);
45135         this.el.slideIn(this.getSlideAnchor(), {
45136             callback: function(){
45137                 this.afterSlide();
45138                 this.initAutoHide();
45139                 Roo.get(document).on("click", this.slideInIf, this);
45140                 this.fireEvent("slideshow", this);
45141             },
45142             scope: this,
45143             block: true
45144         });
45145     },
45146
45147     afterSlideIn : function(){
45148         this.clearAutoHide();
45149         this.isSlid = false;
45150         this.clearMonitor();
45151         this.el.setStyle("z-index", "");
45152         if(this.collapseBtn){
45153             this.collapseBtn.show();
45154         }
45155         this.closeBtn.setStyle('display', this.closeBtnState);
45156         if(this.stickBtn){
45157             this.stickBtn.hide();
45158         }
45159         this.fireEvent("slidehide", this);
45160     },
45161
45162     slideIn : function(cb){
45163         if(!this.isSlid || this.el.hasActiveFx()){
45164             Roo.callback(cb);
45165             return;
45166         }
45167         this.isSlid = false;
45168         this.beforeSlide();
45169         this.el.slideOut(this.getSlideAnchor(), {
45170             callback: function(){
45171                 this.el.setLeftTop(-10000, -10000);
45172                 this.afterSlide();
45173                 this.afterSlideIn();
45174                 Roo.callback(cb);
45175             },
45176             scope: this,
45177             block: true
45178         });
45179     },
45180     
45181     slideInIf : function(e){
45182         if(!e.within(this.el)){
45183             this.slideIn();
45184         }
45185     },
45186
45187     animateCollapse : function(){
45188         this.beforeSlide();
45189         this.el.setStyle("z-index", 20000);
45190         var anchor = this.getSlideAnchor();
45191         this.el.slideOut(anchor, {
45192             callback : function(){
45193                 this.el.setStyle("z-index", "");
45194                 this.collapsedEl.slideIn(anchor, {duration:.3});
45195                 this.afterSlide();
45196                 this.el.setLocation(-10000,-10000);
45197                 this.el.hide();
45198                 this.fireEvent("collapsed", this);
45199             },
45200             scope: this,
45201             block: true
45202         });
45203     },
45204
45205     animateExpand : function(){
45206         this.beforeSlide();
45207         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45208         this.el.setStyle("z-index", 20000);
45209         this.collapsedEl.hide({
45210             duration:.1
45211         });
45212         this.el.slideIn(this.getSlideAnchor(), {
45213             callback : function(){
45214                 this.el.setStyle("z-index", "");
45215                 this.afterSlide();
45216                 if(this.split){
45217                     this.split.el.show();
45218                 }
45219                 this.fireEvent("invalidated", this);
45220                 this.fireEvent("expanded", this);
45221             },
45222             scope: this,
45223             block: true
45224         });
45225     },
45226
45227     anchors : {
45228         "west" : "left",
45229         "east" : "right",
45230         "north" : "top",
45231         "south" : "bottom"
45232     },
45233
45234     sanchors : {
45235         "west" : "l",
45236         "east" : "r",
45237         "north" : "t",
45238         "south" : "b"
45239     },
45240
45241     canchors : {
45242         "west" : "tl-tr",
45243         "east" : "tr-tl",
45244         "north" : "tl-bl",
45245         "south" : "bl-tl"
45246     },
45247
45248     getAnchor : function(){
45249         return this.anchors[this.position];
45250     },
45251
45252     getCollapseAnchor : function(){
45253         return this.canchors[this.position];
45254     },
45255
45256     getSlideAnchor : function(){
45257         return this.sanchors[this.position];
45258     },
45259
45260     getAlignAdj : function(){
45261         var cm = this.cmargins;
45262         switch(this.position){
45263             case "west":
45264                 return [0, 0];
45265             break;
45266             case "east":
45267                 return [0, 0];
45268             break;
45269             case "north":
45270                 return [0, 0];
45271             break;
45272             case "south":
45273                 return [0, 0];
45274             break;
45275         }
45276     },
45277
45278     getExpandAdj : function(){
45279         var c = this.collapsedEl, cm = this.cmargins;
45280         switch(this.position){
45281             case "west":
45282                 return [-(cm.right+c.getWidth()+cm.left), 0];
45283             break;
45284             case "east":
45285                 return [cm.right+c.getWidth()+cm.left, 0];
45286             break;
45287             case "north":
45288                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45289             break;
45290             case "south":
45291                 return [0, cm.top+cm.bottom+c.getHeight()];
45292             break;
45293         }
45294     }
45295 });/*
45296  * Based on:
45297  * Ext JS Library 1.1.1
45298  * Copyright(c) 2006-2007, Ext JS, LLC.
45299  *
45300  * Originally Released Under LGPL - original licence link has changed is not relivant.
45301  *
45302  * Fork - LGPL
45303  * <script type="text/javascript">
45304  */
45305 /*
45306  * These classes are private internal classes
45307  */
45308 Roo.CenterLayoutRegion = function(mgr, config){
45309     Roo.LayoutRegion.call(this, mgr, config, "center");
45310     this.visible = true;
45311     this.minWidth = config.minWidth || 20;
45312     this.minHeight = config.minHeight || 20;
45313 };
45314
45315 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45316     hide : function(){
45317         // center panel can't be hidden
45318     },
45319     
45320     show : function(){
45321         // center panel can't be hidden
45322     },
45323     
45324     getMinWidth: function(){
45325         return this.minWidth;
45326     },
45327     
45328     getMinHeight: function(){
45329         return this.minHeight;
45330     }
45331 });
45332
45333
45334 Roo.NorthLayoutRegion = function(mgr, config){
45335     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45336     if(this.split){
45337         this.split.placement = Roo.SplitBar.TOP;
45338         this.split.orientation = Roo.SplitBar.VERTICAL;
45339         this.split.el.addClass("x-layout-split-v");
45340     }
45341     var size = config.initialSize || config.height;
45342     if(typeof size != "undefined"){
45343         this.el.setHeight(size);
45344     }
45345 };
45346 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45347     orientation: Roo.SplitBar.VERTICAL,
45348     getBox : function(){
45349         if(this.collapsed){
45350             return this.collapsedEl.getBox();
45351         }
45352         var box = this.el.getBox();
45353         if(this.split){
45354             box.height += this.split.el.getHeight();
45355         }
45356         return box;
45357     },
45358     
45359     updateBox : function(box){
45360         if(this.split && !this.collapsed){
45361             box.height -= this.split.el.getHeight();
45362             this.split.el.setLeft(box.x);
45363             this.split.el.setTop(box.y+box.height);
45364             this.split.el.setWidth(box.width);
45365         }
45366         if(this.collapsed){
45367             this.updateBody(box.width, null);
45368         }
45369         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45370     }
45371 });
45372
45373 Roo.SouthLayoutRegion = function(mgr, config){
45374     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45375     if(this.split){
45376         this.split.placement = Roo.SplitBar.BOTTOM;
45377         this.split.orientation = Roo.SplitBar.VERTICAL;
45378         this.split.el.addClass("x-layout-split-v");
45379     }
45380     var size = config.initialSize || config.height;
45381     if(typeof size != "undefined"){
45382         this.el.setHeight(size);
45383     }
45384 };
45385 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45386     orientation: Roo.SplitBar.VERTICAL,
45387     getBox : function(){
45388         if(this.collapsed){
45389             return this.collapsedEl.getBox();
45390         }
45391         var box = this.el.getBox();
45392         if(this.split){
45393             var sh = this.split.el.getHeight();
45394             box.height += sh;
45395             box.y -= sh;
45396         }
45397         return box;
45398     },
45399     
45400     updateBox : function(box){
45401         if(this.split && !this.collapsed){
45402             var sh = this.split.el.getHeight();
45403             box.height -= sh;
45404             box.y += sh;
45405             this.split.el.setLeft(box.x);
45406             this.split.el.setTop(box.y-sh);
45407             this.split.el.setWidth(box.width);
45408         }
45409         if(this.collapsed){
45410             this.updateBody(box.width, null);
45411         }
45412         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45413     }
45414 });
45415
45416 Roo.EastLayoutRegion = function(mgr, config){
45417     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45418     if(this.split){
45419         this.split.placement = Roo.SplitBar.RIGHT;
45420         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45421         this.split.el.addClass("x-layout-split-h");
45422     }
45423     var size = config.initialSize || config.width;
45424     if(typeof size != "undefined"){
45425         this.el.setWidth(size);
45426     }
45427 };
45428 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45429     orientation: Roo.SplitBar.HORIZONTAL,
45430     getBox : function(){
45431         if(this.collapsed){
45432             return this.collapsedEl.getBox();
45433         }
45434         var box = this.el.getBox();
45435         if(this.split){
45436             var sw = this.split.el.getWidth();
45437             box.width += sw;
45438             box.x -= sw;
45439         }
45440         return box;
45441     },
45442
45443     updateBox : function(box){
45444         if(this.split && !this.collapsed){
45445             var sw = this.split.el.getWidth();
45446             box.width -= sw;
45447             this.split.el.setLeft(box.x);
45448             this.split.el.setTop(box.y);
45449             this.split.el.setHeight(box.height);
45450             box.x += sw;
45451         }
45452         if(this.collapsed){
45453             this.updateBody(null, box.height);
45454         }
45455         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45456     }
45457 });
45458
45459 Roo.WestLayoutRegion = function(mgr, config){
45460     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45461     if(this.split){
45462         this.split.placement = Roo.SplitBar.LEFT;
45463         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45464         this.split.el.addClass("x-layout-split-h");
45465     }
45466     var size = config.initialSize || config.width;
45467     if(typeof size != "undefined"){
45468         this.el.setWidth(size);
45469     }
45470 };
45471 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45472     orientation: Roo.SplitBar.HORIZONTAL,
45473     getBox : function(){
45474         if(this.collapsed){
45475             return this.collapsedEl.getBox();
45476         }
45477         var box = this.el.getBox();
45478         if(this.split){
45479             box.width += this.split.el.getWidth();
45480         }
45481         return box;
45482     },
45483     
45484     updateBox : function(box){
45485         if(this.split && !this.collapsed){
45486             var sw = this.split.el.getWidth();
45487             box.width -= sw;
45488             this.split.el.setLeft(box.x+box.width);
45489             this.split.el.setTop(box.y);
45490             this.split.el.setHeight(box.height);
45491         }
45492         if(this.collapsed){
45493             this.updateBody(null, box.height);
45494         }
45495         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45496     }
45497 });
45498 /*
45499  * Based on:
45500  * Ext JS Library 1.1.1
45501  * Copyright(c) 2006-2007, Ext JS, LLC.
45502  *
45503  * Originally Released Under LGPL - original licence link has changed is not relivant.
45504  *
45505  * Fork - LGPL
45506  * <script type="text/javascript">
45507  */
45508  
45509  
45510 /*
45511  * Private internal class for reading and applying state
45512  */
45513 Roo.LayoutStateManager = function(layout){
45514      // default empty state
45515      this.state = {
45516         north: {},
45517         south: {},
45518         east: {},
45519         west: {}       
45520     };
45521 };
45522
45523 Roo.LayoutStateManager.prototype = {
45524     init : function(layout, provider){
45525         this.provider = provider;
45526         var state = provider.get(layout.id+"-layout-state");
45527         if(state){
45528             var wasUpdating = layout.isUpdating();
45529             if(!wasUpdating){
45530                 layout.beginUpdate();
45531             }
45532             for(var key in state){
45533                 if(typeof state[key] != "function"){
45534                     var rstate = state[key];
45535                     var r = layout.getRegion(key);
45536                     if(r && rstate){
45537                         if(rstate.size){
45538                             r.resizeTo(rstate.size);
45539                         }
45540                         if(rstate.collapsed == true){
45541                             r.collapse(true);
45542                         }else{
45543                             r.expand(null, true);
45544                         }
45545                     }
45546                 }
45547             }
45548             if(!wasUpdating){
45549                 layout.endUpdate();
45550             }
45551             this.state = state; 
45552         }
45553         this.layout = layout;
45554         layout.on("regionresized", this.onRegionResized, this);
45555         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45556         layout.on("regionexpanded", this.onRegionExpanded, this);
45557     },
45558     
45559     storeState : function(){
45560         this.provider.set(this.layout.id+"-layout-state", this.state);
45561     },
45562     
45563     onRegionResized : function(region, newSize){
45564         this.state[region.getPosition()].size = newSize;
45565         this.storeState();
45566     },
45567     
45568     onRegionCollapsed : function(region){
45569         this.state[region.getPosition()].collapsed = true;
45570         this.storeState();
45571     },
45572     
45573     onRegionExpanded : function(region){
45574         this.state[region.getPosition()].collapsed = false;
45575         this.storeState();
45576     }
45577 };/*
45578  * Based on:
45579  * Ext JS Library 1.1.1
45580  * Copyright(c) 2006-2007, Ext JS, LLC.
45581  *
45582  * Originally Released Under LGPL - original licence link has changed is not relivant.
45583  *
45584  * Fork - LGPL
45585  * <script type="text/javascript">
45586  */
45587 /**
45588  * @class Roo.ContentPanel
45589  * @extends Roo.util.Observable
45590  * A basic ContentPanel element.
45591  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45592  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45593  * @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
45594  * @cfg {Boolean} closable True if the panel can be closed/removed
45595  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45596  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45597  * @cfg {Toolbar} toolbar A toolbar for this panel
45598  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45599  * @cfg {String} title The title for this panel
45600  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45601  * @cfg {String} url Calls {@link #setUrl} with this value
45602  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45603  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45604  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45605  * @constructor
45606  * Create a new ContentPanel.
45607  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45608  * @param {String/Object} config A string to set only the title or a config object
45609  * @param {String} content (optional) Set the HTML content for this panel
45610  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45611  */
45612 Roo.ContentPanel = function(el, config, content){
45613     
45614      
45615     /*
45616     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45617         config = el;
45618         el = Roo.id();
45619     }
45620     if (config && config.parentLayout) { 
45621         el = config.parentLayout.el.createChild(); 
45622     }
45623     */
45624     if(el.autoCreate){ // xtype is available if this is called from factory
45625         config = el;
45626         el = Roo.id();
45627     }
45628     this.el = Roo.get(el);
45629     if(!this.el && config && config.autoCreate){
45630         if(typeof config.autoCreate == "object"){
45631             if(!config.autoCreate.id){
45632                 config.autoCreate.id = config.id||el;
45633             }
45634             this.el = Roo.DomHelper.append(document.body,
45635                         config.autoCreate, true);
45636         }else{
45637             this.el = Roo.DomHelper.append(document.body,
45638                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45639         }
45640     }
45641     this.closable = false;
45642     this.loaded = false;
45643     this.active = false;
45644     if(typeof config == "string"){
45645         this.title = config;
45646     }else{
45647         Roo.apply(this, config);
45648     }
45649     
45650     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45651         this.wrapEl = this.el.wrap();    
45652         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45653         
45654     }
45655     
45656     
45657     
45658     if(this.resizeEl){
45659         this.resizeEl = Roo.get(this.resizeEl, true);
45660     }else{
45661         this.resizeEl = this.el;
45662     }
45663     this.addEvents({
45664         /**
45665          * @event activate
45666          * Fires when this panel is activated. 
45667          * @param {Roo.ContentPanel} this
45668          */
45669         "activate" : true,
45670         /**
45671          * @event deactivate
45672          * Fires when this panel is activated. 
45673          * @param {Roo.ContentPanel} this
45674          */
45675         "deactivate" : true,
45676
45677         /**
45678          * @event resize
45679          * Fires when this panel is resized if fitToFrame is true.
45680          * @param {Roo.ContentPanel} this
45681          * @param {Number} width The width after any component adjustments
45682          * @param {Number} height The height after any component adjustments
45683          */
45684         "resize" : true
45685     });
45686     if(this.autoScroll){
45687         this.resizeEl.setStyle("overflow", "auto");
45688     } else {
45689         // fix randome scrolling
45690         this.el.on('scroll', function() {
45691             Roo.log('fix random scolling');
45692             this.scrollTo('top',0); 
45693         });
45694     }
45695     content = content || this.content;
45696     if(content){
45697         this.setContent(content);
45698     }
45699     if(config && config.url){
45700         this.setUrl(this.url, this.params, this.loadOnce);
45701     }
45702     
45703     
45704     
45705     Roo.ContentPanel.superclass.constructor.call(this);
45706 };
45707
45708 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45709     tabTip:'',
45710     setRegion : function(region){
45711         this.region = region;
45712         if(region){
45713            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45714         }else{
45715            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45716         } 
45717     },
45718     
45719     /**
45720      * Returns the toolbar for this Panel if one was configured. 
45721      * @return {Roo.Toolbar} 
45722      */
45723     getToolbar : function(){
45724         return this.toolbar;
45725     },
45726     
45727     setActiveState : function(active){
45728         this.active = active;
45729         if(!active){
45730             this.fireEvent("deactivate", this);
45731         }else{
45732             this.fireEvent("activate", this);
45733         }
45734     },
45735     /**
45736      * Updates this panel's element
45737      * @param {String} content The new content
45738      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45739     */
45740     setContent : function(content, loadScripts){
45741         this.el.update(content, loadScripts);
45742     },
45743
45744     ignoreResize : function(w, h){
45745         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45746             return true;
45747         }else{
45748             this.lastSize = {width: w, height: h};
45749             return false;
45750         }
45751     },
45752     /**
45753      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45754      * @return {Roo.UpdateManager} The UpdateManager
45755      */
45756     getUpdateManager : function(){
45757         return this.el.getUpdateManager();
45758     },
45759      /**
45760      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45761      * @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:
45762 <pre><code>
45763 panel.load({
45764     url: "your-url.php",
45765     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45766     callback: yourFunction,
45767     scope: yourObject, //(optional scope)
45768     discardUrl: false,
45769     nocache: false,
45770     text: "Loading...",
45771     timeout: 30,
45772     scripts: false
45773 });
45774 </code></pre>
45775      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45776      * 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.
45777      * @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}
45778      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45779      * @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.
45780      * @return {Roo.ContentPanel} this
45781      */
45782     load : function(){
45783         var um = this.el.getUpdateManager();
45784         um.update.apply(um, arguments);
45785         return this;
45786     },
45787
45788
45789     /**
45790      * 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.
45791      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45792      * @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)
45793      * @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)
45794      * @return {Roo.UpdateManager} The UpdateManager
45795      */
45796     setUrl : function(url, params, loadOnce){
45797         if(this.refreshDelegate){
45798             this.removeListener("activate", this.refreshDelegate);
45799         }
45800         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45801         this.on("activate", this.refreshDelegate);
45802         return this.el.getUpdateManager();
45803     },
45804     
45805     _handleRefresh : function(url, params, loadOnce){
45806         if(!loadOnce || !this.loaded){
45807             var updater = this.el.getUpdateManager();
45808             updater.update(url, params, this._setLoaded.createDelegate(this));
45809         }
45810     },
45811     
45812     _setLoaded : function(){
45813         this.loaded = true;
45814     }, 
45815     
45816     /**
45817      * Returns this panel's id
45818      * @return {String} 
45819      */
45820     getId : function(){
45821         return this.el.id;
45822     },
45823     
45824     /** 
45825      * Returns this panel's element - used by regiosn to add.
45826      * @return {Roo.Element} 
45827      */
45828     getEl : function(){
45829         return this.wrapEl || this.el;
45830     },
45831     
45832     adjustForComponents : function(width, height){
45833         if(this.resizeEl != this.el){
45834             width -= this.el.getFrameWidth('lr');
45835             height -= this.el.getFrameWidth('tb');
45836         }
45837         if(this.toolbar){
45838             var te = this.toolbar.getEl();
45839             height -= te.getHeight();
45840             te.setWidth(width);
45841         }
45842         if(this.adjustments){
45843             width += this.adjustments[0];
45844             height += this.adjustments[1];
45845         }
45846         return {"width": width, "height": height};
45847     },
45848     
45849     setSize : function(width, height){
45850         if(this.fitToFrame && !this.ignoreResize(width, height)){
45851             if(this.fitContainer && this.resizeEl != this.el){
45852                 this.el.setSize(width, height);
45853             }
45854             var size = this.adjustForComponents(width, height);
45855             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45856             this.fireEvent('resize', this, size.width, size.height);
45857         }
45858     },
45859     
45860     /**
45861      * Returns this panel's title
45862      * @return {String} 
45863      */
45864     getTitle : function(){
45865         return this.title;
45866     },
45867     
45868     /**
45869      * Set this panel's title
45870      * @param {String} title
45871      */
45872     setTitle : function(title){
45873         this.title = title;
45874         if(this.region){
45875             this.region.updatePanelTitle(this, title);
45876         }
45877     },
45878     
45879     /**
45880      * Returns true is this panel was configured to be closable
45881      * @return {Boolean} 
45882      */
45883     isClosable : function(){
45884         return this.closable;
45885     },
45886     
45887     beforeSlide : function(){
45888         this.el.clip();
45889         this.resizeEl.clip();
45890     },
45891     
45892     afterSlide : function(){
45893         this.el.unclip();
45894         this.resizeEl.unclip();
45895     },
45896     
45897     /**
45898      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45899      *   Will fail silently if the {@link #setUrl} method has not been called.
45900      *   This does not activate the panel, just updates its content.
45901      */
45902     refresh : function(){
45903         if(this.refreshDelegate){
45904            this.loaded = false;
45905            this.refreshDelegate();
45906         }
45907     },
45908     
45909     /**
45910      * Destroys this panel
45911      */
45912     destroy : function(){
45913         this.el.removeAllListeners();
45914         var tempEl = document.createElement("span");
45915         tempEl.appendChild(this.el.dom);
45916         tempEl.innerHTML = "";
45917         this.el.remove();
45918         this.el = null;
45919     },
45920     
45921       /**
45922      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45923      * <pre><code>
45924
45925 layout.addxtype({
45926        xtype : 'Form',
45927        items: [ .... ]
45928    }
45929 );
45930
45931 </code></pre>
45932      * @param {Object} cfg Xtype definition of item to add.
45933      */
45934     
45935     addxtype : function(cfg) {
45936         // add form..
45937         if (cfg.xtype.match(/^Form$/)) {
45938             var el = this.el.createChild();
45939
45940             this.form = new  Roo.form.Form(cfg);
45941             
45942             
45943             if ( this.form.allItems.length) this.form.render(el.dom);
45944             return this.form;
45945         }
45946         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45947             // views..
45948             cfg.el = this.el.appendChild(document.createElement("div"));
45949             // factory?
45950             var ret = new Roo[cfg.xtype](cfg);
45951             ret.render(false, ''); // render blank..
45952             return ret;
45953             
45954         }
45955         return false;
45956         
45957     }
45958 });
45959
45960 /**
45961  * @class Roo.GridPanel
45962  * @extends Roo.ContentPanel
45963  * @constructor
45964  * Create a new GridPanel.
45965  * @param {Roo.grid.Grid} grid The grid for this panel
45966  * @param {String/Object} config A string to set only the panel's title, or a config object
45967  */
45968 Roo.GridPanel = function(grid, config){
45969     
45970   
45971     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45972         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45973         
45974     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45975     
45976     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45977     
45978     if(this.toolbar){
45979         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45980     }
45981     // xtype created footer. - not sure if will work as we normally have to render first..
45982     if (this.footer && !this.footer.el && this.footer.xtype) {
45983         
45984         this.footer.container = this.grid.getView().getFooterPanel(true);
45985         this.footer.dataSource = this.grid.dataSource;
45986         this.footer = Roo.factory(this.footer, Roo);
45987         
45988     }
45989     
45990     grid.monitorWindowResize = false; // turn off autosizing
45991     grid.autoHeight = false;
45992     grid.autoWidth = false;
45993     this.grid = grid;
45994     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45995 };
45996
45997 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45998     getId : function(){
45999         return this.grid.id;
46000     },
46001     
46002     /**
46003      * Returns the grid for this panel
46004      * @return {Roo.grid.Grid} 
46005      */
46006     getGrid : function(){
46007         return this.grid;    
46008     },
46009     
46010     setSize : function(width, height){
46011         if(!this.ignoreResize(width, height)){
46012             var grid = this.grid;
46013             var size = this.adjustForComponents(width, height);
46014             grid.getGridEl().setSize(size.width, size.height);
46015             grid.autoSize();
46016         }
46017     },
46018     
46019     beforeSlide : function(){
46020         this.grid.getView().scroller.clip();
46021     },
46022     
46023     afterSlide : function(){
46024         this.grid.getView().scroller.unclip();
46025     },
46026     
46027     destroy : function(){
46028         this.grid.destroy();
46029         delete this.grid;
46030         Roo.GridPanel.superclass.destroy.call(this); 
46031     }
46032 });
46033
46034
46035 /**
46036  * @class Roo.NestedLayoutPanel
46037  * @extends Roo.ContentPanel
46038  * @constructor
46039  * Create a new NestedLayoutPanel.
46040  * 
46041  * 
46042  * @param {Roo.BorderLayout} layout The layout for this panel
46043  * @param {String/Object} config A string to set only the title or a config object
46044  */
46045 Roo.NestedLayoutPanel = function(layout, config)
46046 {
46047     // construct with only one argument..
46048     /* FIXME - implement nicer consturctors
46049     if (layout.layout) {
46050         config = layout;
46051         layout = config.layout;
46052         delete config.layout;
46053     }
46054     if (layout.xtype && !layout.getEl) {
46055         // then layout needs constructing..
46056         layout = Roo.factory(layout, Roo);
46057     }
46058     */
46059     
46060     
46061     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46062     
46063     layout.monitorWindowResize = false; // turn off autosizing
46064     this.layout = layout;
46065     this.layout.getEl().addClass("x-layout-nested-layout");
46066     
46067     
46068     
46069     
46070 };
46071
46072 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46073
46074     setSize : function(width, height){
46075         if(!this.ignoreResize(width, height)){
46076             var size = this.adjustForComponents(width, height);
46077             var el = this.layout.getEl();
46078             el.setSize(size.width, size.height);
46079             var touch = el.dom.offsetWidth;
46080             this.layout.layout();
46081             // ie requires a double layout on the first pass
46082             if(Roo.isIE && !this.initialized){
46083                 this.initialized = true;
46084                 this.layout.layout();
46085             }
46086         }
46087     },
46088     
46089     // activate all subpanels if not currently active..
46090     
46091     setActiveState : function(active){
46092         this.active = active;
46093         if(!active){
46094             this.fireEvent("deactivate", this);
46095             return;
46096         }
46097         
46098         this.fireEvent("activate", this);
46099         // not sure if this should happen before or after..
46100         if (!this.layout) {
46101             return; // should not happen..
46102         }
46103         var reg = false;
46104         for (var r in this.layout.regions) {
46105             reg = this.layout.getRegion(r);
46106             if (reg.getActivePanel()) {
46107                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46108                 reg.setActivePanel(reg.getActivePanel());
46109                 continue;
46110             }
46111             if (!reg.panels.length) {
46112                 continue;
46113             }
46114             reg.showPanel(reg.getPanel(0));
46115         }
46116         
46117         
46118         
46119         
46120     },
46121     
46122     /**
46123      * Returns the nested BorderLayout for this panel
46124      * @return {Roo.BorderLayout} 
46125      */
46126     getLayout : function(){
46127         return this.layout;
46128     },
46129     
46130      /**
46131      * Adds a xtype elements to the layout of the nested panel
46132      * <pre><code>
46133
46134 panel.addxtype({
46135        xtype : 'ContentPanel',
46136        region: 'west',
46137        items: [ .... ]
46138    }
46139 );
46140
46141 panel.addxtype({
46142         xtype : 'NestedLayoutPanel',
46143         region: 'west',
46144         layout: {
46145            center: { },
46146            west: { }   
46147         },
46148         items : [ ... list of content panels or nested layout panels.. ]
46149    }
46150 );
46151 </code></pre>
46152      * @param {Object} cfg Xtype definition of item to add.
46153      */
46154     addxtype : function(cfg) {
46155         return this.layout.addxtype(cfg);
46156     
46157     }
46158 });
46159
46160 Roo.ScrollPanel = function(el, config, content){
46161     config = config || {};
46162     config.fitToFrame = true;
46163     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46164     
46165     this.el.dom.style.overflow = "hidden";
46166     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46167     this.el.removeClass("x-layout-inactive-content");
46168     this.el.on("mousewheel", this.onWheel, this);
46169
46170     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46171     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46172     up.unselectable(); down.unselectable();
46173     up.on("click", this.scrollUp, this);
46174     down.on("click", this.scrollDown, this);
46175     up.addClassOnOver("x-scroller-btn-over");
46176     down.addClassOnOver("x-scroller-btn-over");
46177     up.addClassOnClick("x-scroller-btn-click");
46178     down.addClassOnClick("x-scroller-btn-click");
46179     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46180
46181     this.resizeEl = this.el;
46182     this.el = wrap; this.up = up; this.down = down;
46183 };
46184
46185 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46186     increment : 100,
46187     wheelIncrement : 5,
46188     scrollUp : function(){
46189         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46190     },
46191
46192     scrollDown : function(){
46193         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46194     },
46195
46196     afterScroll : function(){
46197         var el = this.resizeEl;
46198         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46199         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46200         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46201     },
46202
46203     setSize : function(){
46204         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46205         this.afterScroll();
46206     },
46207
46208     onWheel : function(e){
46209         var d = e.getWheelDelta();
46210         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46211         this.afterScroll();
46212         e.stopEvent();
46213     },
46214
46215     setContent : function(content, loadScripts){
46216         this.resizeEl.update(content, loadScripts);
46217     }
46218
46219 });
46220
46221
46222
46223
46224
46225
46226
46227
46228
46229 /**
46230  * @class Roo.TreePanel
46231  * @extends Roo.ContentPanel
46232  * @constructor
46233  * Create a new TreePanel. - defaults to fit/scoll contents.
46234  * @param {String/Object} config A string to set only the panel's title, or a config object
46235  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46236  */
46237 Roo.TreePanel = function(config){
46238     var el = config.el;
46239     var tree = config.tree;
46240     delete config.tree; 
46241     delete config.el; // hopefull!
46242     
46243     // wrapper for IE7 strict & safari scroll issue
46244     
46245     var treeEl = el.createChild();
46246     config.resizeEl = treeEl;
46247     
46248     
46249     
46250     Roo.TreePanel.superclass.constructor.call(this, el, config);
46251  
46252  
46253     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46254     //console.log(tree);
46255     this.on('activate', function()
46256     {
46257         if (this.tree.rendered) {
46258             return;
46259         }
46260         //console.log('render tree');
46261         this.tree.render();
46262     });
46263     
46264     this.on('resize',  function (cp, w, h) {
46265             this.tree.innerCt.setWidth(w);
46266             this.tree.innerCt.setHeight(h);
46267             this.tree.innerCt.setStyle('overflow-y', 'auto');
46268     });
46269
46270         
46271     
46272 };
46273
46274 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46275     fitToFrame : true,
46276     autoScroll : true
46277 });
46278
46279
46280
46281
46282
46283
46284
46285
46286
46287
46288
46289 /*
46290  * Based on:
46291  * Ext JS Library 1.1.1
46292  * Copyright(c) 2006-2007, Ext JS, LLC.
46293  *
46294  * Originally Released Under LGPL - original licence link has changed is not relivant.
46295  *
46296  * Fork - LGPL
46297  * <script type="text/javascript">
46298  */
46299  
46300
46301 /**
46302  * @class Roo.ReaderLayout
46303  * @extends Roo.BorderLayout
46304  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46305  * center region containing two nested regions (a top one for a list view and one for item preview below),
46306  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46307  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46308  * expedites the setup of the overall layout and regions for this common application style.
46309  * Example:
46310  <pre><code>
46311 var reader = new Roo.ReaderLayout();
46312 var CP = Roo.ContentPanel;  // shortcut for adding
46313
46314 reader.beginUpdate();
46315 reader.add("north", new CP("north", "North"));
46316 reader.add("west", new CP("west", {title: "West"}));
46317 reader.add("east", new CP("east", {title: "East"}));
46318
46319 reader.regions.listView.add(new CP("listView", "List"));
46320 reader.regions.preview.add(new CP("preview", "Preview"));
46321 reader.endUpdate();
46322 </code></pre>
46323 * @constructor
46324 * Create a new ReaderLayout
46325 * @param {Object} config Configuration options
46326 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46327 * document.body if omitted)
46328 */
46329 Roo.ReaderLayout = function(config, renderTo){
46330     var c = config || {size:{}};
46331     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46332         north: c.north !== false ? Roo.apply({
46333             split:false,
46334             initialSize: 32,
46335             titlebar: false
46336         }, c.north) : false,
46337         west: c.west !== false ? Roo.apply({
46338             split:true,
46339             initialSize: 200,
46340             minSize: 175,
46341             maxSize: 400,
46342             titlebar: true,
46343             collapsible: true,
46344             animate: true,
46345             margins:{left:5,right:0,bottom:5,top:5},
46346             cmargins:{left:5,right:5,bottom:5,top:5}
46347         }, c.west) : false,
46348         east: c.east !== false ? Roo.apply({
46349             split:true,
46350             initialSize: 200,
46351             minSize: 175,
46352             maxSize: 400,
46353             titlebar: true,
46354             collapsible: true,
46355             animate: true,
46356             margins:{left:0,right:5,bottom:5,top:5},
46357             cmargins:{left:5,right:5,bottom:5,top:5}
46358         }, c.east) : false,
46359         center: Roo.apply({
46360             tabPosition: 'top',
46361             autoScroll:false,
46362             closeOnTab: true,
46363             titlebar:false,
46364             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46365         }, c.center)
46366     });
46367
46368     this.el.addClass('x-reader');
46369
46370     this.beginUpdate();
46371
46372     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46373         south: c.preview !== false ? Roo.apply({
46374             split:true,
46375             initialSize: 200,
46376             minSize: 100,
46377             autoScroll:true,
46378             collapsible:true,
46379             titlebar: true,
46380             cmargins:{top:5,left:0, right:0, bottom:0}
46381         }, c.preview) : false,
46382         center: Roo.apply({
46383             autoScroll:false,
46384             titlebar:false,
46385             minHeight:200
46386         }, c.listView)
46387     });
46388     this.add('center', new Roo.NestedLayoutPanel(inner,
46389             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46390
46391     this.endUpdate();
46392
46393     this.regions.preview = inner.getRegion('south');
46394     this.regions.listView = inner.getRegion('center');
46395 };
46396
46397 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46398  * Based on:
46399  * Ext JS Library 1.1.1
46400  * Copyright(c) 2006-2007, Ext JS, LLC.
46401  *
46402  * Originally Released Under LGPL - original licence link has changed is not relivant.
46403  *
46404  * Fork - LGPL
46405  * <script type="text/javascript">
46406  */
46407  
46408 /**
46409  * @class Roo.grid.Grid
46410  * @extends Roo.util.Observable
46411  * This class represents the primary interface of a component based grid control.
46412  * <br><br>Usage:<pre><code>
46413  var grid = new Roo.grid.Grid("my-container-id", {
46414      ds: myDataStore,
46415      cm: myColModel,
46416      selModel: mySelectionModel,
46417      autoSizeColumns: true,
46418      monitorWindowResize: false,
46419      trackMouseOver: true
46420  });
46421  // set any options
46422  grid.render();
46423  * </code></pre>
46424  * <b>Common Problems:</b><br/>
46425  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46426  * element will correct this<br/>
46427  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46428  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46429  * are unpredictable.<br/>
46430  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46431  * grid to calculate dimensions/offsets.<br/>
46432   * @constructor
46433  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46434  * The container MUST have some type of size defined for the grid to fill. The container will be
46435  * automatically set to position relative if it isn't already.
46436  * @param {Object} config A config object that sets properties on this grid.
46437  */
46438 Roo.grid.Grid = function(container, config){
46439         // initialize the container
46440         this.container = Roo.get(container);
46441         this.container.update("");
46442         this.container.setStyle("overflow", "hidden");
46443     this.container.addClass('x-grid-container');
46444
46445     this.id = this.container.id;
46446
46447     Roo.apply(this, config);
46448     // check and correct shorthanded configs
46449     if(this.ds){
46450         this.dataSource = this.ds;
46451         delete this.ds;
46452     }
46453     if(this.cm){
46454         this.colModel = this.cm;
46455         delete this.cm;
46456     }
46457     if(this.sm){
46458         this.selModel = this.sm;
46459         delete this.sm;
46460     }
46461
46462     if (this.selModel) {
46463         this.selModel = Roo.factory(this.selModel, Roo.grid);
46464         this.sm = this.selModel;
46465         this.sm.xmodule = this.xmodule || false;
46466     }
46467     if (typeof(this.colModel.config) == 'undefined') {
46468         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46469         this.cm = this.colModel;
46470         this.cm.xmodule = this.xmodule || false;
46471     }
46472     if (this.dataSource) {
46473         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46474         this.ds = this.dataSource;
46475         this.ds.xmodule = this.xmodule || false;
46476          
46477     }
46478     
46479     
46480     
46481     if(this.width){
46482         this.container.setWidth(this.width);
46483     }
46484
46485     if(this.height){
46486         this.container.setHeight(this.height);
46487     }
46488     /** @private */
46489         this.addEvents({
46490         // raw events
46491         /**
46492          * @event click
46493          * The raw click event for the entire grid.
46494          * @param {Roo.EventObject} e
46495          */
46496         "click" : true,
46497         /**
46498          * @event dblclick
46499          * The raw dblclick event for the entire grid.
46500          * @param {Roo.EventObject} e
46501          */
46502         "dblclick" : true,
46503         /**
46504          * @event contextmenu
46505          * The raw contextmenu event for the entire grid.
46506          * @param {Roo.EventObject} e
46507          */
46508         "contextmenu" : true,
46509         /**
46510          * @event mousedown
46511          * The raw mousedown event for the entire grid.
46512          * @param {Roo.EventObject} e
46513          */
46514         "mousedown" : true,
46515         /**
46516          * @event mouseup
46517          * The raw mouseup event for the entire grid.
46518          * @param {Roo.EventObject} e
46519          */
46520         "mouseup" : true,
46521         /**
46522          * @event mouseover
46523          * The raw mouseover event for the entire grid.
46524          * @param {Roo.EventObject} e
46525          */
46526         "mouseover" : true,
46527         /**
46528          * @event mouseout
46529          * The raw mouseout event for the entire grid.
46530          * @param {Roo.EventObject} e
46531          */
46532         "mouseout" : true,
46533         /**
46534          * @event keypress
46535          * The raw keypress event for the entire grid.
46536          * @param {Roo.EventObject} e
46537          */
46538         "keypress" : true,
46539         /**
46540          * @event keydown
46541          * The raw keydown event for the entire grid.
46542          * @param {Roo.EventObject} e
46543          */
46544         "keydown" : true,
46545
46546         // custom events
46547
46548         /**
46549          * @event cellclick
46550          * Fires when a cell is clicked
46551          * @param {Grid} this
46552          * @param {Number} rowIndex
46553          * @param {Number} columnIndex
46554          * @param {Roo.EventObject} e
46555          */
46556         "cellclick" : true,
46557         /**
46558          * @event celldblclick
46559          * Fires when a cell is double clicked
46560          * @param {Grid} this
46561          * @param {Number} rowIndex
46562          * @param {Number} columnIndex
46563          * @param {Roo.EventObject} e
46564          */
46565         "celldblclick" : true,
46566         /**
46567          * @event rowclick
46568          * Fires when a row is clicked
46569          * @param {Grid} this
46570          * @param {Number} rowIndex
46571          * @param {Roo.EventObject} e
46572          */
46573         "rowclick" : true,
46574         /**
46575          * @event rowdblclick
46576          * Fires when a row is double clicked
46577          * @param {Grid} this
46578          * @param {Number} rowIndex
46579          * @param {Roo.EventObject} e
46580          */
46581         "rowdblclick" : true,
46582         /**
46583          * @event headerclick
46584          * Fires when a header is clicked
46585          * @param {Grid} this
46586          * @param {Number} columnIndex
46587          * @param {Roo.EventObject} e
46588          */
46589         "headerclick" : true,
46590         /**
46591          * @event headerdblclick
46592          * Fires when a header cell is double clicked
46593          * @param {Grid} this
46594          * @param {Number} columnIndex
46595          * @param {Roo.EventObject} e
46596          */
46597         "headerdblclick" : true,
46598         /**
46599          * @event rowcontextmenu
46600          * Fires when a row is right clicked
46601          * @param {Grid} this
46602          * @param {Number} rowIndex
46603          * @param {Roo.EventObject} e
46604          */
46605         "rowcontextmenu" : true,
46606         /**
46607          * @event cellcontextmenu
46608          * Fires when a cell is right clicked
46609          * @param {Grid} this
46610          * @param {Number} rowIndex
46611          * @param {Number} cellIndex
46612          * @param {Roo.EventObject} e
46613          */
46614          "cellcontextmenu" : true,
46615         /**
46616          * @event headercontextmenu
46617          * Fires when a header is right clicked
46618          * @param {Grid} this
46619          * @param {Number} columnIndex
46620          * @param {Roo.EventObject} e
46621          */
46622         "headercontextmenu" : true,
46623         /**
46624          * @event bodyscroll
46625          * Fires when the body element is scrolled
46626          * @param {Number} scrollLeft
46627          * @param {Number} scrollTop
46628          */
46629         "bodyscroll" : true,
46630         /**
46631          * @event columnresize
46632          * Fires when the user resizes a column
46633          * @param {Number} columnIndex
46634          * @param {Number} newSize
46635          */
46636         "columnresize" : true,
46637         /**
46638          * @event columnmove
46639          * Fires when the user moves a column
46640          * @param {Number} oldIndex
46641          * @param {Number} newIndex
46642          */
46643         "columnmove" : true,
46644         /**
46645          * @event startdrag
46646          * Fires when row(s) start being dragged
46647          * @param {Grid} this
46648          * @param {Roo.GridDD} dd The drag drop object
46649          * @param {event} e The raw browser event
46650          */
46651         "startdrag" : true,
46652         /**
46653          * @event enddrag
46654          * Fires when a drag operation is complete
46655          * @param {Grid} this
46656          * @param {Roo.GridDD} dd The drag drop object
46657          * @param {event} e The raw browser event
46658          */
46659         "enddrag" : true,
46660         /**
46661          * @event dragdrop
46662          * Fires when dragged row(s) are dropped on a valid DD target
46663          * @param {Grid} this
46664          * @param {Roo.GridDD} dd The drag drop object
46665          * @param {String} targetId The target drag drop object
46666          * @param {event} e The raw browser event
46667          */
46668         "dragdrop" : true,
46669         /**
46670          * @event dragover
46671          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46672          * @param {Grid} this
46673          * @param {Roo.GridDD} dd The drag drop object
46674          * @param {String} targetId The target drag drop object
46675          * @param {event} e The raw browser event
46676          */
46677         "dragover" : true,
46678         /**
46679          * @event dragenter
46680          *  Fires when the dragged row(s) first cross another DD target while being dragged
46681          * @param {Grid} this
46682          * @param {Roo.GridDD} dd The drag drop object
46683          * @param {String} targetId The target drag drop object
46684          * @param {event} e The raw browser event
46685          */
46686         "dragenter" : true,
46687         /**
46688          * @event dragout
46689          * Fires when the dragged row(s) leave another DD target while being dragged
46690          * @param {Grid} this
46691          * @param {Roo.GridDD} dd The drag drop object
46692          * @param {String} targetId The target drag drop object
46693          * @param {event} e The raw browser event
46694          */
46695         "dragout" : true,
46696         /**
46697          * @event rowclass
46698          * Fires when a row is rendered, so you can change add a style to it.
46699          * @param {GridView} gridview   The grid view
46700          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46701          */
46702         'rowclass' : true,
46703
46704         /**
46705          * @event render
46706          * Fires when the grid is rendered
46707          * @param {Grid} grid
46708          */
46709         'render' : true
46710     });
46711
46712     Roo.grid.Grid.superclass.constructor.call(this);
46713 };
46714 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46715     
46716     /**
46717      * @cfg {String} ddGroup - drag drop group.
46718      */
46719
46720     /**
46721      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46722      */
46723     minColumnWidth : 25,
46724
46725     /**
46726      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46727      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46728      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46729      */
46730     autoSizeColumns : false,
46731
46732     /**
46733      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46734      */
46735     autoSizeHeaders : true,
46736
46737     /**
46738      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46739      */
46740     monitorWindowResize : true,
46741
46742     /**
46743      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46744      * rows measured to get a columns size. Default is 0 (all rows).
46745      */
46746     maxRowsToMeasure : 0,
46747
46748     /**
46749      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46750      */
46751     trackMouseOver : true,
46752
46753     /**
46754     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46755     */
46756     
46757     /**
46758     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46759     */
46760     enableDragDrop : false,
46761     
46762     /**
46763     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46764     */
46765     enableColumnMove : true,
46766     
46767     /**
46768     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46769     */
46770     enableColumnHide : true,
46771     
46772     /**
46773     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46774     */
46775     enableRowHeightSync : false,
46776     
46777     /**
46778     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46779     */
46780     stripeRows : true,
46781     
46782     /**
46783     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46784     */
46785     autoHeight : false,
46786
46787     /**
46788      * @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.
46789      */
46790     autoExpandColumn : false,
46791
46792     /**
46793     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46794     * Default is 50.
46795     */
46796     autoExpandMin : 50,
46797
46798     /**
46799     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46800     */
46801     autoExpandMax : 1000,
46802
46803     /**
46804     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46805     */
46806     view : null,
46807
46808     /**
46809     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46810     */
46811     loadMask : false,
46812     /**
46813     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46814     */
46815     dropTarget: false,
46816     
46817    
46818     
46819     // private
46820     rendered : false,
46821
46822     /**
46823     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46824     * of a fixed width. Default is false.
46825     */
46826     /**
46827     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46828     */
46829     /**
46830      * Called once after all setup has been completed and the grid is ready to be rendered.
46831      * @return {Roo.grid.Grid} this
46832      */
46833     render : function()
46834     {
46835         var c = this.container;
46836         // try to detect autoHeight/width mode
46837         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46838             this.autoHeight = true;
46839         }
46840         var view = this.getView();
46841         view.init(this);
46842
46843         c.on("click", this.onClick, this);
46844         c.on("dblclick", this.onDblClick, this);
46845         c.on("contextmenu", this.onContextMenu, this);
46846         c.on("keydown", this.onKeyDown, this);
46847
46848         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46849
46850         this.getSelectionModel().init(this);
46851
46852         view.render();
46853
46854         if(this.loadMask){
46855             this.loadMask = new Roo.LoadMask(this.container,
46856                     Roo.apply({store:this.dataSource}, this.loadMask));
46857         }
46858         
46859         
46860         if (this.toolbar && this.toolbar.xtype) {
46861             this.toolbar.container = this.getView().getHeaderPanel(true);
46862             this.toolbar = new Roo.Toolbar(this.toolbar);
46863         }
46864         if (this.footer && this.footer.xtype) {
46865             this.footer.dataSource = this.getDataSource();
46866             this.footer.container = this.getView().getFooterPanel(true);
46867             this.footer = Roo.factory(this.footer, Roo);
46868         }
46869         if (this.dropTarget && this.dropTarget.xtype) {
46870             delete this.dropTarget.xtype;
46871             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46872         }
46873         
46874         
46875         this.rendered = true;
46876         this.fireEvent('render', this);
46877         return this;
46878     },
46879
46880         /**
46881          * Reconfigures the grid to use a different Store and Column Model.
46882          * The View will be bound to the new objects and refreshed.
46883          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46884          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46885          */
46886     reconfigure : function(dataSource, colModel){
46887         if(this.loadMask){
46888             this.loadMask.destroy();
46889             this.loadMask = new Roo.LoadMask(this.container,
46890                     Roo.apply({store:dataSource}, this.loadMask));
46891         }
46892         this.view.bind(dataSource, colModel);
46893         this.dataSource = dataSource;
46894         this.colModel = colModel;
46895         this.view.refresh(true);
46896     },
46897
46898     // private
46899     onKeyDown : function(e){
46900         this.fireEvent("keydown", e);
46901     },
46902
46903     /**
46904      * Destroy this grid.
46905      * @param {Boolean} removeEl True to remove the element
46906      */
46907     destroy : function(removeEl, keepListeners){
46908         if(this.loadMask){
46909             this.loadMask.destroy();
46910         }
46911         var c = this.container;
46912         c.removeAllListeners();
46913         this.view.destroy();
46914         this.colModel.purgeListeners();
46915         if(!keepListeners){
46916             this.purgeListeners();
46917         }
46918         c.update("");
46919         if(removeEl === true){
46920             c.remove();
46921         }
46922     },
46923
46924     // private
46925     processEvent : function(name, e){
46926         this.fireEvent(name, e);
46927         var t = e.getTarget();
46928         var v = this.view;
46929         var header = v.findHeaderIndex(t);
46930         if(header !== false){
46931             this.fireEvent("header" + name, this, header, e);
46932         }else{
46933             var row = v.findRowIndex(t);
46934             var cell = v.findCellIndex(t);
46935             if(row !== false){
46936                 this.fireEvent("row" + name, this, row, e);
46937                 if(cell !== false){
46938                     this.fireEvent("cell" + name, this, row, cell, e);
46939                 }
46940             }
46941         }
46942     },
46943
46944     // private
46945     onClick : function(e){
46946         this.processEvent("click", e);
46947     },
46948
46949     // private
46950     onContextMenu : function(e, t){
46951         this.processEvent("contextmenu", e);
46952     },
46953
46954     // private
46955     onDblClick : function(e){
46956         this.processEvent("dblclick", e);
46957     },
46958
46959     // private
46960     walkCells : function(row, col, step, fn, scope){
46961         var cm = this.colModel, clen = cm.getColumnCount();
46962         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46963         if(step < 0){
46964             if(col < 0){
46965                 row--;
46966                 first = false;
46967             }
46968             while(row >= 0){
46969                 if(!first){
46970                     col = clen-1;
46971                 }
46972                 first = false;
46973                 while(col >= 0){
46974                     if(fn.call(scope || this, row, col, cm) === true){
46975                         return [row, col];
46976                     }
46977                     col--;
46978                 }
46979                 row--;
46980             }
46981         } else {
46982             if(col >= clen){
46983                 row++;
46984                 first = false;
46985             }
46986             while(row < rlen){
46987                 if(!first){
46988                     col = 0;
46989                 }
46990                 first = false;
46991                 while(col < clen){
46992                     if(fn.call(scope || this, row, col, cm) === true){
46993                         return [row, col];
46994                     }
46995                     col++;
46996                 }
46997                 row++;
46998             }
46999         }
47000         return null;
47001     },
47002
47003     // private
47004     getSelections : function(){
47005         return this.selModel.getSelections();
47006     },
47007
47008     /**
47009      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47010      * but if manual update is required this method will initiate it.
47011      */
47012     autoSize : function(){
47013         if(this.rendered){
47014             this.view.layout();
47015             if(this.view.adjustForScroll){
47016                 this.view.adjustForScroll();
47017             }
47018         }
47019     },
47020
47021     /**
47022      * Returns the grid's underlying element.
47023      * @return {Element} The element
47024      */
47025     getGridEl : function(){
47026         return this.container;
47027     },
47028
47029     // private for compatibility, overridden by editor grid
47030     stopEditing : function(){},
47031
47032     /**
47033      * Returns the grid's SelectionModel.
47034      * @return {SelectionModel}
47035      */
47036     getSelectionModel : function(){
47037         if(!this.selModel){
47038             this.selModel = new Roo.grid.RowSelectionModel();
47039         }
47040         return this.selModel;
47041     },
47042
47043     /**
47044      * Returns the grid's DataSource.
47045      * @return {DataSource}
47046      */
47047     getDataSource : function(){
47048         return this.dataSource;
47049     },
47050
47051     /**
47052      * Returns the grid's ColumnModel.
47053      * @return {ColumnModel}
47054      */
47055     getColumnModel : function(){
47056         return this.colModel;
47057     },
47058
47059     /**
47060      * Returns the grid's GridView object.
47061      * @return {GridView}
47062      */
47063     getView : function(){
47064         if(!this.view){
47065             this.view = new Roo.grid.GridView(this.viewConfig);
47066         }
47067         return this.view;
47068     },
47069     /**
47070      * Called to get grid's drag proxy text, by default returns this.ddText.
47071      * @return {String}
47072      */
47073     getDragDropText : function(){
47074         var count = this.selModel.getCount();
47075         return String.format(this.ddText, count, count == 1 ? '' : 's');
47076     }
47077 });
47078 /**
47079  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47080  * %0 is replaced with the number of selected rows.
47081  * @type String
47082  */
47083 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47084  * Based on:
47085  * Ext JS Library 1.1.1
47086  * Copyright(c) 2006-2007, Ext JS, LLC.
47087  *
47088  * Originally Released Under LGPL - original licence link has changed is not relivant.
47089  *
47090  * Fork - LGPL
47091  * <script type="text/javascript">
47092  */
47093  
47094 Roo.grid.AbstractGridView = function(){
47095         this.grid = null;
47096         
47097         this.events = {
47098             "beforerowremoved" : true,
47099             "beforerowsinserted" : true,
47100             "beforerefresh" : true,
47101             "rowremoved" : true,
47102             "rowsinserted" : true,
47103             "rowupdated" : true,
47104             "refresh" : true
47105         };
47106     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47107 };
47108
47109 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47110     rowClass : "x-grid-row",
47111     cellClass : "x-grid-cell",
47112     tdClass : "x-grid-td",
47113     hdClass : "x-grid-hd",
47114     splitClass : "x-grid-hd-split",
47115     
47116         init: function(grid){
47117         this.grid = grid;
47118                 var cid = this.grid.getGridEl().id;
47119         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47120         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47121         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47122         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47123         },
47124         
47125         getColumnRenderers : function(){
47126         var renderers = [];
47127         var cm = this.grid.colModel;
47128         var colCount = cm.getColumnCount();
47129         for(var i = 0; i < colCount; i++){
47130             renderers[i] = cm.getRenderer(i);
47131         }
47132         return renderers;
47133     },
47134     
47135     getColumnIds : function(){
47136         var ids = [];
47137         var cm = this.grid.colModel;
47138         var colCount = cm.getColumnCount();
47139         for(var i = 0; i < colCount; i++){
47140             ids[i] = cm.getColumnId(i);
47141         }
47142         return ids;
47143     },
47144     
47145     getDataIndexes : function(){
47146         if(!this.indexMap){
47147             this.indexMap = this.buildIndexMap();
47148         }
47149         return this.indexMap.colToData;
47150     },
47151     
47152     getColumnIndexByDataIndex : function(dataIndex){
47153         if(!this.indexMap){
47154             this.indexMap = this.buildIndexMap();
47155         }
47156         return this.indexMap.dataToCol[dataIndex];
47157     },
47158     
47159     /**
47160      * Set a css style for a column dynamically. 
47161      * @param {Number} colIndex The index of the column
47162      * @param {String} name The css property name
47163      * @param {String} value The css value
47164      */
47165     setCSSStyle : function(colIndex, name, value){
47166         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47167         Roo.util.CSS.updateRule(selector, name, value);
47168     },
47169     
47170     generateRules : function(cm){
47171         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47172         Roo.util.CSS.removeStyleSheet(rulesId);
47173         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47174             var cid = cm.getColumnId(i);
47175             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47176                          this.tdSelector, cid, " {\n}\n",
47177                          this.hdSelector, cid, " {\n}\n",
47178                          this.splitSelector, cid, " {\n}\n");
47179         }
47180         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47181     }
47182 });/*
47183  * Based on:
47184  * Ext JS Library 1.1.1
47185  * Copyright(c) 2006-2007, Ext JS, LLC.
47186  *
47187  * Originally Released Under LGPL - original licence link has changed is not relivant.
47188  *
47189  * Fork - LGPL
47190  * <script type="text/javascript">
47191  */
47192
47193 // private
47194 // This is a support class used internally by the Grid components
47195 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47196     this.grid = grid;
47197     this.view = grid.getView();
47198     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47199     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47200     if(hd2){
47201         this.setHandleElId(Roo.id(hd));
47202         this.setOuterHandleElId(Roo.id(hd2));
47203     }
47204     this.scroll = false;
47205 };
47206 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47207     maxDragWidth: 120,
47208     getDragData : function(e){
47209         var t = Roo.lib.Event.getTarget(e);
47210         var h = this.view.findHeaderCell(t);
47211         if(h){
47212             return {ddel: h.firstChild, header:h};
47213         }
47214         return false;
47215     },
47216
47217     onInitDrag : function(e){
47218         this.view.headersDisabled = true;
47219         var clone = this.dragData.ddel.cloneNode(true);
47220         clone.id = Roo.id();
47221         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47222         this.proxy.update(clone);
47223         return true;
47224     },
47225
47226     afterValidDrop : function(){
47227         var v = this.view;
47228         setTimeout(function(){
47229             v.headersDisabled = false;
47230         }, 50);
47231     },
47232
47233     afterInvalidDrop : function(){
47234         var v = this.view;
47235         setTimeout(function(){
47236             v.headersDisabled = false;
47237         }, 50);
47238     }
47239 });
47240 /*
47241  * Based on:
47242  * Ext JS Library 1.1.1
47243  * Copyright(c) 2006-2007, Ext JS, LLC.
47244  *
47245  * Originally Released Under LGPL - original licence link has changed is not relivant.
47246  *
47247  * Fork - LGPL
47248  * <script type="text/javascript">
47249  */
47250 // private
47251 // This is a support class used internally by the Grid components
47252 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47253     this.grid = grid;
47254     this.view = grid.getView();
47255     // split the proxies so they don't interfere with mouse events
47256     this.proxyTop = Roo.DomHelper.append(document.body, {
47257         cls:"col-move-top", html:"&#160;"
47258     }, true);
47259     this.proxyBottom = Roo.DomHelper.append(document.body, {
47260         cls:"col-move-bottom", html:"&#160;"
47261     }, true);
47262     this.proxyTop.hide = this.proxyBottom.hide = function(){
47263         this.setLeftTop(-100,-100);
47264         this.setStyle("visibility", "hidden");
47265     };
47266     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47267     // temporarily disabled
47268     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47269     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47270 };
47271 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47272     proxyOffsets : [-4, -9],
47273     fly: Roo.Element.fly,
47274
47275     getTargetFromEvent : function(e){
47276         var t = Roo.lib.Event.getTarget(e);
47277         var cindex = this.view.findCellIndex(t);
47278         if(cindex !== false){
47279             return this.view.getHeaderCell(cindex);
47280         }
47281         return null;
47282     },
47283
47284     nextVisible : function(h){
47285         var v = this.view, cm = this.grid.colModel;
47286         h = h.nextSibling;
47287         while(h){
47288             if(!cm.isHidden(v.getCellIndex(h))){
47289                 return h;
47290             }
47291             h = h.nextSibling;
47292         }
47293         return null;
47294     },
47295
47296     prevVisible : function(h){
47297         var v = this.view, cm = this.grid.colModel;
47298         h = h.prevSibling;
47299         while(h){
47300             if(!cm.isHidden(v.getCellIndex(h))){
47301                 return h;
47302             }
47303             h = h.prevSibling;
47304         }
47305         return null;
47306     },
47307
47308     positionIndicator : function(h, n, e){
47309         var x = Roo.lib.Event.getPageX(e);
47310         var r = Roo.lib.Dom.getRegion(n.firstChild);
47311         var px, pt, py = r.top + this.proxyOffsets[1];
47312         if((r.right - x) <= (r.right-r.left)/2){
47313             px = r.right+this.view.borderWidth;
47314             pt = "after";
47315         }else{
47316             px = r.left;
47317             pt = "before";
47318         }
47319         var oldIndex = this.view.getCellIndex(h);
47320         var newIndex = this.view.getCellIndex(n);
47321
47322         if(this.grid.colModel.isFixed(newIndex)){
47323             return false;
47324         }
47325
47326         var locked = this.grid.colModel.isLocked(newIndex);
47327
47328         if(pt == "after"){
47329             newIndex++;
47330         }
47331         if(oldIndex < newIndex){
47332             newIndex--;
47333         }
47334         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47335             return false;
47336         }
47337         px +=  this.proxyOffsets[0];
47338         this.proxyTop.setLeftTop(px, py);
47339         this.proxyTop.show();
47340         if(!this.bottomOffset){
47341             this.bottomOffset = this.view.mainHd.getHeight();
47342         }
47343         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47344         this.proxyBottom.show();
47345         return pt;
47346     },
47347
47348     onNodeEnter : function(n, dd, e, data){
47349         if(data.header != n){
47350             this.positionIndicator(data.header, n, e);
47351         }
47352     },
47353
47354     onNodeOver : function(n, dd, e, data){
47355         var result = false;
47356         if(data.header != n){
47357             result = this.positionIndicator(data.header, n, e);
47358         }
47359         if(!result){
47360             this.proxyTop.hide();
47361             this.proxyBottom.hide();
47362         }
47363         return result ? this.dropAllowed : this.dropNotAllowed;
47364     },
47365
47366     onNodeOut : function(n, dd, e, data){
47367         this.proxyTop.hide();
47368         this.proxyBottom.hide();
47369     },
47370
47371     onNodeDrop : function(n, dd, e, data){
47372         var h = data.header;
47373         if(h != n){
47374             var cm = this.grid.colModel;
47375             var x = Roo.lib.Event.getPageX(e);
47376             var r = Roo.lib.Dom.getRegion(n.firstChild);
47377             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47378             var oldIndex = this.view.getCellIndex(h);
47379             var newIndex = this.view.getCellIndex(n);
47380             var locked = cm.isLocked(newIndex);
47381             if(pt == "after"){
47382                 newIndex++;
47383             }
47384             if(oldIndex < newIndex){
47385                 newIndex--;
47386             }
47387             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47388                 return false;
47389             }
47390             cm.setLocked(oldIndex, locked, true);
47391             cm.moveColumn(oldIndex, newIndex);
47392             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47393             return true;
47394         }
47395         return false;
47396     }
47397 });
47398 /*
47399  * Based on:
47400  * Ext JS Library 1.1.1
47401  * Copyright(c) 2006-2007, Ext JS, LLC.
47402  *
47403  * Originally Released Under LGPL - original licence link has changed is not relivant.
47404  *
47405  * Fork - LGPL
47406  * <script type="text/javascript">
47407  */
47408   
47409 /**
47410  * @class Roo.grid.GridView
47411  * @extends Roo.util.Observable
47412  *
47413  * @constructor
47414  * @param {Object} config
47415  */
47416 Roo.grid.GridView = function(config){
47417     Roo.grid.GridView.superclass.constructor.call(this);
47418     this.el = null;
47419
47420     Roo.apply(this, config);
47421 };
47422
47423 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47424
47425     /**
47426      * Override this function to apply custom css classes to rows during rendering
47427      * @param {Record} record The record
47428      * @param {Number} index
47429      * @method getRowClass
47430      */
47431     rowClass : "x-grid-row",
47432
47433     cellClass : "x-grid-col",
47434
47435     tdClass : "x-grid-td",
47436
47437     hdClass : "x-grid-hd",
47438
47439     splitClass : "x-grid-split",
47440
47441     sortClasses : ["sort-asc", "sort-desc"],
47442
47443     enableMoveAnim : false,
47444
47445     hlColor: "C3DAF9",
47446
47447     dh : Roo.DomHelper,
47448
47449     fly : Roo.Element.fly,
47450
47451     css : Roo.util.CSS,
47452
47453     borderWidth: 1,
47454
47455     splitOffset: 3,
47456
47457     scrollIncrement : 22,
47458
47459     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47460
47461     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47462
47463     bind : function(ds, cm){
47464         if(this.ds){
47465             this.ds.un("load", this.onLoad, this);
47466             this.ds.un("datachanged", this.onDataChange, this);
47467             this.ds.un("add", this.onAdd, this);
47468             this.ds.un("remove", this.onRemove, this);
47469             this.ds.un("update", this.onUpdate, this);
47470             this.ds.un("clear", this.onClear, this);
47471         }
47472         if(ds){
47473             ds.on("load", this.onLoad, this);
47474             ds.on("datachanged", this.onDataChange, this);
47475             ds.on("add", this.onAdd, this);
47476             ds.on("remove", this.onRemove, this);
47477             ds.on("update", this.onUpdate, this);
47478             ds.on("clear", this.onClear, this);
47479         }
47480         this.ds = ds;
47481
47482         if(this.cm){
47483             this.cm.un("widthchange", this.onColWidthChange, this);
47484             this.cm.un("headerchange", this.onHeaderChange, this);
47485             this.cm.un("hiddenchange", this.onHiddenChange, this);
47486             this.cm.un("columnmoved", this.onColumnMove, this);
47487             this.cm.un("columnlockchange", this.onColumnLock, this);
47488         }
47489         if(cm){
47490             this.generateRules(cm);
47491             cm.on("widthchange", this.onColWidthChange, this);
47492             cm.on("headerchange", this.onHeaderChange, this);
47493             cm.on("hiddenchange", this.onHiddenChange, this);
47494             cm.on("columnmoved", this.onColumnMove, this);
47495             cm.on("columnlockchange", this.onColumnLock, this);
47496         }
47497         this.cm = cm;
47498     },
47499
47500     init: function(grid){
47501         Roo.grid.GridView.superclass.init.call(this, grid);
47502
47503         this.bind(grid.dataSource, grid.colModel);
47504
47505         grid.on("headerclick", this.handleHeaderClick, this);
47506
47507         if(grid.trackMouseOver){
47508             grid.on("mouseover", this.onRowOver, this);
47509             grid.on("mouseout", this.onRowOut, this);
47510         }
47511         grid.cancelTextSelection = function(){};
47512         this.gridId = grid.id;
47513
47514         var tpls = this.templates || {};
47515
47516         if(!tpls.master){
47517             tpls.master = new Roo.Template(
47518                '<div class="x-grid" hidefocus="true">',
47519                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47520                   '<div class="x-grid-topbar"></div>',
47521                   '<div class="x-grid-scroller"><div></div></div>',
47522                   '<div class="x-grid-locked">',
47523                       '<div class="x-grid-header">{lockedHeader}</div>',
47524                       '<div class="x-grid-body">{lockedBody}</div>',
47525                   "</div>",
47526                   '<div class="x-grid-viewport">',
47527                       '<div class="x-grid-header">{header}</div>',
47528                       '<div class="x-grid-body">{body}</div>',
47529                   "</div>",
47530                   '<div class="x-grid-bottombar"></div>',
47531                  
47532                   '<div class="x-grid-resize-proxy">&#160;</div>',
47533                "</div>"
47534             );
47535             tpls.master.disableformats = true;
47536         }
47537
47538         if(!tpls.header){
47539             tpls.header = new Roo.Template(
47540                '<table border="0" cellspacing="0" cellpadding="0">',
47541                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47542                "</table>{splits}"
47543             );
47544             tpls.header.disableformats = true;
47545         }
47546         tpls.header.compile();
47547
47548         if(!tpls.hcell){
47549             tpls.hcell = new Roo.Template(
47550                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47551                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47552                 "</div></td>"
47553              );
47554              tpls.hcell.disableFormats = true;
47555         }
47556         tpls.hcell.compile();
47557
47558         if(!tpls.hsplit){
47559             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47560             tpls.hsplit.disableFormats = true;
47561         }
47562         tpls.hsplit.compile();
47563
47564         if(!tpls.body){
47565             tpls.body = new Roo.Template(
47566                '<table border="0" cellspacing="0" cellpadding="0">',
47567                "<tbody>{rows}</tbody>",
47568                "</table>"
47569             );
47570             tpls.body.disableFormats = true;
47571         }
47572         tpls.body.compile();
47573
47574         if(!tpls.row){
47575             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47576             tpls.row.disableFormats = true;
47577         }
47578         tpls.row.compile();
47579
47580         if(!tpls.cell){
47581             tpls.cell = new Roo.Template(
47582                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47583                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47584                 "</td>"
47585             );
47586             tpls.cell.disableFormats = true;
47587         }
47588         tpls.cell.compile();
47589
47590         this.templates = tpls;
47591     },
47592
47593     // remap these for backwards compat
47594     onColWidthChange : function(){
47595         this.updateColumns.apply(this, arguments);
47596     },
47597     onHeaderChange : function(){
47598         this.updateHeaders.apply(this, arguments);
47599     }, 
47600     onHiddenChange : function(){
47601         this.handleHiddenChange.apply(this, arguments);
47602     },
47603     onColumnMove : function(){
47604         this.handleColumnMove.apply(this, arguments);
47605     },
47606     onColumnLock : function(){
47607         this.handleLockChange.apply(this, arguments);
47608     },
47609
47610     onDataChange : function(){
47611         this.refresh();
47612         this.updateHeaderSortState();
47613     },
47614
47615     onClear : function(){
47616         this.refresh();
47617     },
47618
47619     onUpdate : function(ds, record){
47620         this.refreshRow(record);
47621     },
47622
47623     refreshRow : function(record){
47624         var ds = this.ds, index;
47625         if(typeof record == 'number'){
47626             index = record;
47627             record = ds.getAt(index);
47628         }else{
47629             index = ds.indexOf(record);
47630         }
47631         this.insertRows(ds, index, index, true);
47632         this.onRemove(ds, record, index+1, true);
47633         this.syncRowHeights(index, index);
47634         this.layout();
47635         this.fireEvent("rowupdated", this, index, record);
47636     },
47637
47638     onAdd : function(ds, records, index){
47639         this.insertRows(ds, index, index + (records.length-1));
47640     },
47641
47642     onRemove : function(ds, record, index, isUpdate){
47643         if(isUpdate !== true){
47644             this.fireEvent("beforerowremoved", this, index, record);
47645         }
47646         var bt = this.getBodyTable(), lt = this.getLockedTable();
47647         if(bt.rows[index]){
47648             bt.firstChild.removeChild(bt.rows[index]);
47649         }
47650         if(lt.rows[index]){
47651             lt.firstChild.removeChild(lt.rows[index]);
47652         }
47653         if(isUpdate !== true){
47654             this.stripeRows(index);
47655             this.syncRowHeights(index, index);
47656             this.layout();
47657             this.fireEvent("rowremoved", this, index, record);
47658         }
47659     },
47660
47661     onLoad : function(){
47662         this.scrollToTop();
47663     },
47664
47665     /**
47666      * Scrolls the grid to the top
47667      */
47668     scrollToTop : function(){
47669         if(this.scroller){
47670             this.scroller.dom.scrollTop = 0;
47671             this.syncScroll();
47672         }
47673     },
47674
47675     /**
47676      * Gets a panel in the header of the grid that can be used for toolbars etc.
47677      * After modifying the contents of this panel a call to grid.autoSize() may be
47678      * required to register any changes in size.
47679      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47680      * @return Roo.Element
47681      */
47682     getHeaderPanel : function(doShow){
47683         if(doShow){
47684             this.headerPanel.show();
47685         }
47686         return this.headerPanel;
47687     },
47688
47689     /**
47690      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47691      * After modifying the contents of this panel a call to grid.autoSize() may be
47692      * required to register any changes in size.
47693      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47694      * @return Roo.Element
47695      */
47696     getFooterPanel : function(doShow){
47697         if(doShow){
47698             this.footerPanel.show();
47699         }
47700         return this.footerPanel;
47701     },
47702
47703     initElements : function(){
47704         var E = Roo.Element;
47705         var el = this.grid.getGridEl().dom.firstChild;
47706         var cs = el.childNodes;
47707
47708         this.el = new E(el);
47709         
47710          this.focusEl = new E(el.firstChild);
47711         this.focusEl.swallowEvent("click", true);
47712         
47713         this.headerPanel = new E(cs[1]);
47714         this.headerPanel.enableDisplayMode("block");
47715
47716         this.scroller = new E(cs[2]);
47717         this.scrollSizer = new E(this.scroller.dom.firstChild);
47718
47719         this.lockedWrap = new E(cs[3]);
47720         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47721         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47722
47723         this.mainWrap = new E(cs[4]);
47724         this.mainHd = new E(this.mainWrap.dom.firstChild);
47725         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47726
47727         this.footerPanel = new E(cs[5]);
47728         this.footerPanel.enableDisplayMode("block");
47729
47730         this.resizeProxy = new E(cs[6]);
47731
47732         this.headerSelector = String.format(
47733            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47734            this.lockedHd.id, this.mainHd.id
47735         );
47736
47737         this.splitterSelector = String.format(
47738            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47739            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47740         );
47741     },
47742     idToCssName : function(s)
47743     {
47744         return s.replace(/[^a-z0-9]+/ig, '-');
47745     },
47746
47747     getHeaderCell : function(index){
47748         return Roo.DomQuery.select(this.headerSelector)[index];
47749     },
47750
47751     getHeaderCellMeasure : function(index){
47752         return this.getHeaderCell(index).firstChild;
47753     },
47754
47755     getHeaderCellText : function(index){
47756         return this.getHeaderCell(index).firstChild.firstChild;
47757     },
47758
47759     getLockedTable : function(){
47760         return this.lockedBody.dom.firstChild;
47761     },
47762
47763     getBodyTable : function(){
47764         return this.mainBody.dom.firstChild;
47765     },
47766
47767     getLockedRow : function(index){
47768         return this.getLockedTable().rows[index];
47769     },
47770
47771     getRow : function(index){
47772         return this.getBodyTable().rows[index];
47773     },
47774
47775     getRowComposite : function(index){
47776         if(!this.rowEl){
47777             this.rowEl = new Roo.CompositeElementLite();
47778         }
47779         var els = [], lrow, mrow;
47780         if(lrow = this.getLockedRow(index)){
47781             els.push(lrow);
47782         }
47783         if(mrow = this.getRow(index)){
47784             els.push(mrow);
47785         }
47786         this.rowEl.elements = els;
47787         return this.rowEl;
47788     },
47789
47790     getCell : function(rowIndex, colIndex){
47791         var locked = this.cm.getLockedCount();
47792         var source;
47793         if(colIndex < locked){
47794             source = this.lockedBody.dom.firstChild;
47795         }else{
47796             source = this.mainBody.dom.firstChild;
47797             colIndex -= locked;
47798         }
47799         return source.rows[rowIndex].childNodes[colIndex];
47800     },
47801
47802     getCellText : function(rowIndex, colIndex){
47803         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47804     },
47805
47806     getCellBox : function(cell){
47807         var b = this.fly(cell).getBox();
47808         if(Roo.isOpera){ // opera fails to report the Y
47809             b.y = cell.offsetTop + this.mainBody.getY();
47810         }
47811         return b;
47812     },
47813
47814     getCellIndex : function(cell){
47815         var id = String(cell.className).match(this.cellRE);
47816         if(id){
47817             return parseInt(id[1], 10);
47818         }
47819         return 0;
47820     },
47821
47822     findHeaderIndex : function(n){
47823         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47824         return r ? this.getCellIndex(r) : false;
47825     },
47826
47827     findHeaderCell : function(n){
47828         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47829         return r ? r : false;
47830     },
47831
47832     findRowIndex : function(n){
47833         if(!n){
47834             return false;
47835         }
47836         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47837         return r ? r.rowIndex : false;
47838     },
47839
47840     findCellIndex : function(node){
47841         var stop = this.el.dom;
47842         while(node && node != stop){
47843             if(this.findRE.test(node.className)){
47844                 return this.getCellIndex(node);
47845             }
47846             node = node.parentNode;
47847         }
47848         return false;
47849     },
47850
47851     getColumnId : function(index){
47852         return this.cm.getColumnId(index);
47853     },
47854
47855     getSplitters : function()
47856     {
47857         if(this.splitterSelector){
47858            return Roo.DomQuery.select(this.splitterSelector);
47859         }else{
47860             return null;
47861       }
47862     },
47863
47864     getSplitter : function(index){
47865         return this.getSplitters()[index];
47866     },
47867
47868     onRowOver : function(e, t){
47869         var row;
47870         if((row = this.findRowIndex(t)) !== false){
47871             this.getRowComposite(row).addClass("x-grid-row-over");
47872         }
47873     },
47874
47875     onRowOut : function(e, t){
47876         var row;
47877         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47878             this.getRowComposite(row).removeClass("x-grid-row-over");
47879         }
47880     },
47881
47882     renderHeaders : function(){
47883         var cm = this.cm;
47884         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47885         var cb = [], lb = [], sb = [], lsb = [], p = {};
47886         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47887             p.cellId = "x-grid-hd-0-" + i;
47888             p.splitId = "x-grid-csplit-0-" + i;
47889             p.id = cm.getColumnId(i);
47890             p.title = cm.getColumnTooltip(i) || "";
47891             p.value = cm.getColumnHeader(i) || "";
47892             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47893             if(!cm.isLocked(i)){
47894                 cb[cb.length] = ct.apply(p);
47895                 sb[sb.length] = st.apply(p);
47896             }else{
47897                 lb[lb.length] = ct.apply(p);
47898                 lsb[lsb.length] = st.apply(p);
47899             }
47900         }
47901         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47902                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47903     },
47904
47905     updateHeaders : function(){
47906         var html = this.renderHeaders();
47907         this.lockedHd.update(html[0]);
47908         this.mainHd.update(html[1]);
47909     },
47910
47911     /**
47912      * Focuses the specified row.
47913      * @param {Number} row The row index
47914      */
47915     focusRow : function(row)
47916     {
47917         //Roo.log('GridView.focusRow');
47918         var x = this.scroller.dom.scrollLeft;
47919         this.focusCell(row, 0, false);
47920         this.scroller.dom.scrollLeft = x;
47921     },
47922
47923     /**
47924      * Focuses the specified cell.
47925      * @param {Number} row The row index
47926      * @param {Number} col The column index
47927      * @param {Boolean} hscroll false to disable horizontal scrolling
47928      */
47929     focusCell : function(row, col, hscroll)
47930     {
47931         //Roo.log('GridView.focusCell');
47932         var el = this.ensureVisible(row, col, hscroll);
47933         this.focusEl.alignTo(el, "tl-tl");
47934         if(Roo.isGecko){
47935             this.focusEl.focus();
47936         }else{
47937             this.focusEl.focus.defer(1, this.focusEl);
47938         }
47939     },
47940
47941     /**
47942      * Scrolls the specified cell into view
47943      * @param {Number} row The row index
47944      * @param {Number} col The column index
47945      * @param {Boolean} hscroll false to disable horizontal scrolling
47946      */
47947     ensureVisible : function(row, col, hscroll)
47948     {
47949         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47950         //return null; //disable for testing.
47951         if(typeof row != "number"){
47952             row = row.rowIndex;
47953         }
47954         if(row < 0 && row >= this.ds.getCount()){
47955             return  null;
47956         }
47957         col = (col !== undefined ? col : 0);
47958         var cm = this.grid.colModel;
47959         while(cm.isHidden(col)){
47960             col++;
47961         }
47962
47963         var el = this.getCell(row, col);
47964         if(!el){
47965             return null;
47966         }
47967         var c = this.scroller.dom;
47968
47969         var ctop = parseInt(el.offsetTop, 10);
47970         var cleft = parseInt(el.offsetLeft, 10);
47971         var cbot = ctop + el.offsetHeight;
47972         var cright = cleft + el.offsetWidth;
47973         
47974         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47975         var stop = parseInt(c.scrollTop, 10);
47976         var sleft = parseInt(c.scrollLeft, 10);
47977         var sbot = stop + ch;
47978         var sright = sleft + c.clientWidth;
47979         /*
47980         Roo.log('GridView.ensureVisible:' +
47981                 ' ctop:' + ctop +
47982                 ' c.clientHeight:' + c.clientHeight +
47983                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
47984                 ' stop:' + stop +
47985                 ' cbot:' + cbot +
47986                 ' sbot:' + sbot +
47987                 ' ch:' + ch  
47988                 );
47989         */
47990         if(ctop < stop){
47991              c.scrollTop = ctop;
47992             //Roo.log("set scrolltop to ctop DISABLE?");
47993         }else if(cbot > sbot){
47994             //Roo.log("set scrolltop to cbot-ch");
47995             c.scrollTop = cbot-ch;
47996         }
47997         
47998         if(hscroll !== false){
47999             if(cleft < sleft){
48000                 c.scrollLeft = cleft;
48001             }else if(cright > sright){
48002                 c.scrollLeft = cright-c.clientWidth;
48003             }
48004         }
48005          
48006         return el;
48007     },
48008
48009     updateColumns : function(){
48010         this.grid.stopEditing();
48011         var cm = this.grid.colModel, colIds = this.getColumnIds();
48012         //var totalWidth = cm.getTotalWidth();
48013         var pos = 0;
48014         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48015             //if(cm.isHidden(i)) continue;
48016             var w = cm.getColumnWidth(i);
48017             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48018             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48019         }
48020         this.updateSplitters();
48021     },
48022
48023     generateRules : function(cm){
48024         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48025         Roo.util.CSS.removeStyleSheet(rulesId);
48026         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48027             var cid = cm.getColumnId(i);
48028             var align = '';
48029             if(cm.config[i].align){
48030                 align = 'text-align:'+cm.config[i].align+';';
48031             }
48032             var hidden = '';
48033             if(cm.isHidden(i)){
48034                 hidden = 'display:none;';
48035             }
48036             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48037             ruleBuf.push(
48038                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48039                     this.hdSelector, cid, " {\n", align, width, "}\n",
48040                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48041                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48042         }
48043         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48044     },
48045
48046     updateSplitters : function(){
48047         var cm = this.cm, s = this.getSplitters();
48048         if(s){ // splitters not created yet
48049             var pos = 0, locked = true;
48050             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48051                 if(cm.isHidden(i)) continue;
48052                 var w = cm.getColumnWidth(i); // make sure it's a number
48053                 if(!cm.isLocked(i) && locked){
48054                     pos = 0;
48055                     locked = false;
48056                 }
48057                 pos += w;
48058                 s[i].style.left = (pos-this.splitOffset) + "px";
48059             }
48060         }
48061     },
48062
48063     handleHiddenChange : function(colModel, colIndex, hidden){
48064         if(hidden){
48065             this.hideColumn(colIndex);
48066         }else{
48067             this.unhideColumn(colIndex);
48068         }
48069     },
48070
48071     hideColumn : function(colIndex){
48072         var cid = this.getColumnId(colIndex);
48073         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48074         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48075         if(Roo.isSafari){
48076             this.updateHeaders();
48077         }
48078         this.updateSplitters();
48079         this.layout();
48080     },
48081
48082     unhideColumn : function(colIndex){
48083         var cid = this.getColumnId(colIndex);
48084         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48085         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48086
48087         if(Roo.isSafari){
48088             this.updateHeaders();
48089         }
48090         this.updateSplitters();
48091         this.layout();
48092     },
48093
48094     insertRows : function(dm, firstRow, lastRow, isUpdate){
48095         if(firstRow == 0 && lastRow == dm.getCount()-1){
48096             this.refresh();
48097         }else{
48098             if(!isUpdate){
48099                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48100             }
48101             var s = this.getScrollState();
48102             var markup = this.renderRows(firstRow, lastRow);
48103             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48104             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48105             this.restoreScroll(s);
48106             if(!isUpdate){
48107                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48108                 this.syncRowHeights(firstRow, lastRow);
48109                 this.stripeRows(firstRow);
48110                 this.layout();
48111             }
48112         }
48113     },
48114
48115     bufferRows : function(markup, target, index){
48116         var before = null, trows = target.rows, tbody = target.tBodies[0];
48117         if(index < trows.length){
48118             before = trows[index];
48119         }
48120         var b = document.createElement("div");
48121         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48122         var rows = b.firstChild.rows;
48123         for(var i = 0, len = rows.length; i < len; i++){
48124             if(before){
48125                 tbody.insertBefore(rows[0], before);
48126             }else{
48127                 tbody.appendChild(rows[0]);
48128             }
48129         }
48130         b.innerHTML = "";
48131         b = null;
48132     },
48133
48134     deleteRows : function(dm, firstRow, lastRow){
48135         if(dm.getRowCount()<1){
48136             this.fireEvent("beforerefresh", this);
48137             this.mainBody.update("");
48138             this.lockedBody.update("");
48139             this.fireEvent("refresh", this);
48140         }else{
48141             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48142             var bt = this.getBodyTable();
48143             var tbody = bt.firstChild;
48144             var rows = bt.rows;
48145             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48146                 tbody.removeChild(rows[firstRow]);
48147             }
48148             this.stripeRows(firstRow);
48149             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48150         }
48151     },
48152
48153     updateRows : function(dataSource, firstRow, lastRow){
48154         var s = this.getScrollState();
48155         this.refresh();
48156         this.restoreScroll(s);
48157     },
48158
48159     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48160         if(!noRefresh){
48161            this.refresh();
48162         }
48163         this.updateHeaderSortState();
48164     },
48165
48166     getScrollState : function(){
48167         
48168         var sb = this.scroller.dom;
48169         return {left: sb.scrollLeft, top: sb.scrollTop};
48170     },
48171
48172     stripeRows : function(startRow){
48173         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48174             return;
48175         }
48176         startRow = startRow || 0;
48177         var rows = this.getBodyTable().rows;
48178         var lrows = this.getLockedTable().rows;
48179         var cls = ' x-grid-row-alt ';
48180         for(var i = startRow, len = rows.length; i < len; i++){
48181             var row = rows[i], lrow = lrows[i];
48182             var isAlt = ((i+1) % 2 == 0);
48183             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48184             if(isAlt == hasAlt){
48185                 continue;
48186             }
48187             if(isAlt){
48188                 row.className += " x-grid-row-alt";
48189             }else{
48190                 row.className = row.className.replace("x-grid-row-alt", "");
48191             }
48192             if(lrow){
48193                 lrow.className = row.className;
48194             }
48195         }
48196     },
48197
48198     restoreScroll : function(state){
48199         //Roo.log('GridView.restoreScroll');
48200         var sb = this.scroller.dom;
48201         sb.scrollLeft = state.left;
48202         sb.scrollTop = state.top;
48203         this.syncScroll();
48204     },
48205
48206     syncScroll : function(){
48207         //Roo.log('GridView.syncScroll');
48208         var sb = this.scroller.dom;
48209         var sh = this.mainHd.dom;
48210         var bs = this.mainBody.dom;
48211         var lv = this.lockedBody.dom;
48212         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48213         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48214     },
48215
48216     handleScroll : function(e){
48217         this.syncScroll();
48218         var sb = this.scroller.dom;
48219         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48220         e.stopEvent();
48221     },
48222
48223     handleWheel : function(e){
48224         var d = e.getWheelDelta();
48225         this.scroller.dom.scrollTop -= d*22;
48226         // set this here to prevent jumpy scrolling on large tables
48227         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48228         e.stopEvent();
48229     },
48230
48231     renderRows : function(startRow, endRow){
48232         // pull in all the crap needed to render rows
48233         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48234         var colCount = cm.getColumnCount();
48235
48236         if(ds.getCount() < 1){
48237             return ["", ""];
48238         }
48239
48240         // build a map for all the columns
48241         var cs = [];
48242         for(var i = 0; i < colCount; i++){
48243             var name = cm.getDataIndex(i);
48244             cs[i] = {
48245                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48246                 renderer : cm.getRenderer(i),
48247                 id : cm.getColumnId(i),
48248                 locked : cm.isLocked(i)
48249             };
48250         }
48251
48252         startRow = startRow || 0;
48253         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48254
48255         // records to render
48256         var rs = ds.getRange(startRow, endRow);
48257
48258         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48259     },
48260
48261     // As much as I hate to duplicate code, this was branched because FireFox really hates
48262     // [].join("") on strings. The performance difference was substantial enough to
48263     // branch this function
48264     doRender : Roo.isGecko ?
48265             function(cs, rs, ds, startRow, colCount, stripe){
48266                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48267                 // buffers
48268                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48269                 
48270                 var hasListener = this.grid.hasListener('rowclass');
48271                 var rowcfg = {};
48272                 for(var j = 0, len = rs.length; j < len; j++){
48273                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48274                     for(var i = 0; i < colCount; i++){
48275                         c = cs[i];
48276                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48277                         p.id = c.id;
48278                         p.css = p.attr = "";
48279                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48280                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48281                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48282                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48283                         }
48284                         var markup = ct.apply(p);
48285                         if(!c.locked){
48286                             cb+= markup;
48287                         }else{
48288                             lcb+= markup;
48289                         }
48290                     }
48291                     var alt = [];
48292                     if(stripe && ((rowIndex+1) % 2 == 0)){
48293                         alt.push("x-grid-row-alt")
48294                     }
48295                     if(r.dirty){
48296                         alt.push(  " x-grid-dirty-row");
48297                     }
48298                     rp.cells = lcb;
48299                     if(this.getRowClass){
48300                         alt.push(this.getRowClass(r, rowIndex));
48301                     }
48302                     if (hasListener) {
48303                         rowcfg = {
48304                              
48305                             record: r,
48306                             rowIndex : rowIndex,
48307                             rowClass : ''
48308                         }
48309                         this.grid.fireEvent('rowclass', this, rowcfg);
48310                         alt.push(rowcfg.rowClass);
48311                     }
48312                     rp.alt = alt.join(" ");
48313                     lbuf+= rt.apply(rp);
48314                     rp.cells = cb;
48315                     buf+=  rt.apply(rp);
48316                 }
48317                 return [lbuf, buf];
48318             } :
48319             function(cs, rs, ds, startRow, colCount, stripe){
48320                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48321                 // buffers
48322                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48323                 var hasListener = this.grid.hasListener('rowclass');
48324                 var rowcfg = {};
48325                 for(var j = 0, len = rs.length; j < len; j++){
48326                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48327                     for(var i = 0; i < colCount; i++){
48328                         c = cs[i];
48329                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48330                         p.id = c.id;
48331                         p.css = p.attr = "";
48332                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48333                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48334                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48335                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48336                         }
48337                         var markup = ct.apply(p);
48338                         if(!c.locked){
48339                             cb[cb.length] = markup;
48340                         }else{
48341                             lcb[lcb.length] = markup;
48342                         }
48343                     }
48344                     var alt = [];
48345                     if(stripe && ((rowIndex+1) % 2 == 0)){
48346                         alt.push( "x-grid-row-alt");
48347                     }
48348                     if(r.dirty){
48349                         alt.push(" x-grid-dirty-row");
48350                     }
48351                     rp.cells = lcb;
48352                     if(this.getRowClass){
48353                         alt.push( this.getRowClass(r, rowIndex));
48354                     }
48355                     if (hasListener) {
48356                         rowcfg = {
48357                              
48358                             record: r,
48359                             rowIndex : rowIndex,
48360                             rowClass : ''
48361                         }
48362                         this.grid.fireEvent('rowclass', this, rowcfg);
48363                         alt.push(rowcfg.rowClass);
48364                     }
48365                     rp.alt = alt.join(" ");
48366                     rp.cells = lcb.join("");
48367                     lbuf[lbuf.length] = rt.apply(rp);
48368                     rp.cells = cb.join("");
48369                     buf[buf.length] =  rt.apply(rp);
48370                 }
48371                 return [lbuf.join(""), buf.join("")];
48372             },
48373
48374     renderBody : function(){
48375         var markup = this.renderRows();
48376         var bt = this.templates.body;
48377         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48378     },
48379
48380     /**
48381      * Refreshes the grid
48382      * @param {Boolean} headersToo
48383      */
48384     refresh : function(headersToo){
48385         this.fireEvent("beforerefresh", this);
48386         this.grid.stopEditing();
48387         var result = this.renderBody();
48388         this.lockedBody.update(result[0]);
48389         this.mainBody.update(result[1]);
48390         if(headersToo === true){
48391             this.updateHeaders();
48392             this.updateColumns();
48393             this.updateSplitters();
48394             this.updateHeaderSortState();
48395         }
48396         this.syncRowHeights();
48397         this.layout();
48398         this.fireEvent("refresh", this);
48399     },
48400
48401     handleColumnMove : function(cm, oldIndex, newIndex){
48402         this.indexMap = null;
48403         var s = this.getScrollState();
48404         this.refresh(true);
48405         this.restoreScroll(s);
48406         this.afterMove(newIndex);
48407     },
48408
48409     afterMove : function(colIndex){
48410         if(this.enableMoveAnim && Roo.enableFx){
48411             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48412         }
48413         // if multisort - fix sortOrder, and reload..
48414         if (this.grid.dataSource.multiSort) {
48415             // the we can call sort again..
48416             var dm = this.grid.dataSource;
48417             var cm = this.grid.colModel;
48418             var so = [];
48419             for(var i = 0; i < cm.config.length; i++ ) {
48420                 
48421                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48422                     continue; // dont' bother, it's not in sort list or being set.
48423                 }
48424                 
48425                 so.push(cm.config[i].dataIndex);
48426             };
48427             dm.sortOrder = so;
48428             dm.load(dm.lastOptions);
48429             
48430             
48431         }
48432         
48433     },
48434
48435     updateCell : function(dm, rowIndex, dataIndex){
48436         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48437         if(typeof colIndex == "undefined"){ // not present in grid
48438             return;
48439         }
48440         var cm = this.grid.colModel;
48441         var cell = this.getCell(rowIndex, colIndex);
48442         var cellText = this.getCellText(rowIndex, colIndex);
48443
48444         var p = {
48445             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48446             id : cm.getColumnId(colIndex),
48447             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48448         };
48449         var renderer = cm.getRenderer(colIndex);
48450         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48451         if(typeof val == "undefined" || val === "") val = "&#160;";
48452         cellText.innerHTML = val;
48453         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48454         this.syncRowHeights(rowIndex, rowIndex);
48455     },
48456
48457     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48458         var maxWidth = 0;
48459         if(this.grid.autoSizeHeaders){
48460             var h = this.getHeaderCellMeasure(colIndex);
48461             maxWidth = Math.max(maxWidth, h.scrollWidth);
48462         }
48463         var tb, index;
48464         if(this.cm.isLocked(colIndex)){
48465             tb = this.getLockedTable();
48466             index = colIndex;
48467         }else{
48468             tb = this.getBodyTable();
48469             index = colIndex - this.cm.getLockedCount();
48470         }
48471         if(tb && tb.rows){
48472             var rows = tb.rows;
48473             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48474             for(var i = 0; i < stopIndex; i++){
48475                 var cell = rows[i].childNodes[index].firstChild;
48476                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48477             }
48478         }
48479         return maxWidth + /*margin for error in IE*/ 5;
48480     },
48481     /**
48482      * Autofit a column to its content.
48483      * @param {Number} colIndex
48484      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48485      */
48486      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48487          if(this.cm.isHidden(colIndex)){
48488              return; // can't calc a hidden column
48489          }
48490         if(forceMinSize){
48491             var cid = this.cm.getColumnId(colIndex);
48492             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48493            if(this.grid.autoSizeHeaders){
48494                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48495            }
48496         }
48497         var newWidth = this.calcColumnWidth(colIndex);
48498         this.cm.setColumnWidth(colIndex,
48499             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48500         if(!suppressEvent){
48501             this.grid.fireEvent("columnresize", colIndex, newWidth);
48502         }
48503     },
48504
48505     /**
48506      * Autofits all columns to their content and then expands to fit any extra space in the grid
48507      */
48508      autoSizeColumns : function(){
48509         var cm = this.grid.colModel;
48510         var colCount = cm.getColumnCount();
48511         for(var i = 0; i < colCount; i++){
48512             this.autoSizeColumn(i, true, true);
48513         }
48514         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48515             this.fitColumns();
48516         }else{
48517             this.updateColumns();
48518             this.layout();
48519         }
48520     },
48521
48522     /**
48523      * Autofits all columns to the grid's width proportionate with their current size
48524      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48525      */
48526     fitColumns : function(reserveScrollSpace){
48527         var cm = this.grid.colModel;
48528         var colCount = cm.getColumnCount();
48529         var cols = [];
48530         var width = 0;
48531         var i, w;
48532         for (i = 0; i < colCount; i++){
48533             if(!cm.isHidden(i) && !cm.isFixed(i)){
48534                 w = cm.getColumnWidth(i);
48535                 cols.push(i);
48536                 cols.push(w);
48537                 width += w;
48538             }
48539         }
48540         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48541         if(reserveScrollSpace){
48542             avail -= 17;
48543         }
48544         var frac = (avail - cm.getTotalWidth())/width;
48545         while (cols.length){
48546             w = cols.pop();
48547             i = cols.pop();
48548             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48549         }
48550         this.updateColumns();
48551         this.layout();
48552     },
48553
48554     onRowSelect : function(rowIndex){
48555         var row = this.getRowComposite(rowIndex);
48556         row.addClass("x-grid-row-selected");
48557     },
48558
48559     onRowDeselect : function(rowIndex){
48560         var row = this.getRowComposite(rowIndex);
48561         row.removeClass("x-grid-row-selected");
48562     },
48563
48564     onCellSelect : function(row, col){
48565         var cell = this.getCell(row, col);
48566         if(cell){
48567             Roo.fly(cell).addClass("x-grid-cell-selected");
48568         }
48569     },
48570
48571     onCellDeselect : function(row, col){
48572         var cell = this.getCell(row, col);
48573         if(cell){
48574             Roo.fly(cell).removeClass("x-grid-cell-selected");
48575         }
48576     },
48577
48578     updateHeaderSortState : function(){
48579         
48580         // sort state can be single { field: xxx, direction : yyy}
48581         // or   { xxx=>ASC , yyy : DESC ..... }
48582         
48583         var mstate = {};
48584         if (!this.ds.multiSort) { 
48585             var state = this.ds.getSortState();
48586             if(!state){
48587                 return;
48588             }
48589             mstate[state.field] = state.direction;
48590             // FIXME... - this is not used here.. but might be elsewhere..
48591             this.sortState = state;
48592             
48593         } else {
48594             mstate = this.ds.sortToggle;
48595         }
48596         //remove existing sort classes..
48597         
48598         var sc = this.sortClasses;
48599         var hds = this.el.select(this.headerSelector).removeClass(sc);
48600         
48601         for(var f in mstate) {
48602         
48603             var sortColumn = this.cm.findColumnIndex(f);
48604             
48605             if(sortColumn != -1){
48606                 var sortDir = mstate[f];        
48607                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48608             }
48609         }
48610         
48611          
48612         
48613     },
48614
48615
48616     handleHeaderClick : function(g, index){
48617         if(this.headersDisabled){
48618             return;
48619         }
48620         var dm = g.dataSource, cm = g.colModel;
48621         if(!cm.isSortable(index)){
48622             return;
48623         }
48624         g.stopEditing();
48625         
48626         if (dm.multiSort) {
48627             // update the sortOrder
48628             var so = [];
48629             for(var i = 0; i < cm.config.length; i++ ) {
48630                 
48631                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48632                     continue; // dont' bother, it's not in sort list or being set.
48633                 }
48634                 
48635                 so.push(cm.config[i].dataIndex);
48636             };
48637             dm.sortOrder = so;
48638         }
48639         
48640         
48641         dm.sort(cm.getDataIndex(index));
48642     },
48643
48644
48645     destroy : function(){
48646         if(this.colMenu){
48647             this.colMenu.removeAll();
48648             Roo.menu.MenuMgr.unregister(this.colMenu);
48649             this.colMenu.getEl().remove();
48650             delete this.colMenu;
48651         }
48652         if(this.hmenu){
48653             this.hmenu.removeAll();
48654             Roo.menu.MenuMgr.unregister(this.hmenu);
48655             this.hmenu.getEl().remove();
48656             delete this.hmenu;
48657         }
48658         if(this.grid.enableColumnMove){
48659             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48660             if(dds){
48661                 for(var dd in dds){
48662                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48663                         var elid = dds[dd].dragElId;
48664                         dds[dd].unreg();
48665                         Roo.get(elid).remove();
48666                     } else if(dds[dd].config.isTarget){
48667                         dds[dd].proxyTop.remove();
48668                         dds[dd].proxyBottom.remove();
48669                         dds[dd].unreg();
48670                     }
48671                     if(Roo.dd.DDM.locationCache[dd]){
48672                         delete Roo.dd.DDM.locationCache[dd];
48673                     }
48674                 }
48675                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48676             }
48677         }
48678         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48679         this.bind(null, null);
48680         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48681     },
48682
48683     handleLockChange : function(){
48684         this.refresh(true);
48685     },
48686
48687     onDenyColumnLock : function(){
48688
48689     },
48690
48691     onDenyColumnHide : function(){
48692
48693     },
48694
48695     handleHdMenuClick : function(item){
48696         var index = this.hdCtxIndex;
48697         var cm = this.cm, ds = this.ds;
48698         switch(item.id){
48699             case "asc":
48700                 ds.sort(cm.getDataIndex(index), "ASC");
48701                 break;
48702             case "desc":
48703                 ds.sort(cm.getDataIndex(index), "DESC");
48704                 break;
48705             case "lock":
48706                 var lc = cm.getLockedCount();
48707                 if(cm.getColumnCount(true) <= lc+1){
48708                     this.onDenyColumnLock();
48709                     return;
48710                 }
48711                 if(lc != index){
48712                     cm.setLocked(index, true, true);
48713                     cm.moveColumn(index, lc);
48714                     this.grid.fireEvent("columnmove", index, lc);
48715                 }else{
48716                     cm.setLocked(index, true);
48717                 }
48718             break;
48719             case "unlock":
48720                 var lc = cm.getLockedCount();
48721                 if((lc-1) != index){
48722                     cm.setLocked(index, false, true);
48723                     cm.moveColumn(index, lc-1);
48724                     this.grid.fireEvent("columnmove", index, lc-1);
48725                 }else{
48726                     cm.setLocked(index, false);
48727                 }
48728             break;
48729             default:
48730                 index = cm.getIndexById(item.id.substr(4));
48731                 if(index != -1){
48732                     if(item.checked && cm.getColumnCount(true) <= 1){
48733                         this.onDenyColumnHide();
48734                         return false;
48735                     }
48736                     cm.setHidden(index, item.checked);
48737                 }
48738         }
48739         return true;
48740     },
48741
48742     beforeColMenuShow : function(){
48743         var cm = this.cm,  colCount = cm.getColumnCount();
48744         this.colMenu.removeAll();
48745         for(var i = 0; i < colCount; i++){
48746             this.colMenu.add(new Roo.menu.CheckItem({
48747                 id: "col-"+cm.getColumnId(i),
48748                 text: cm.getColumnHeader(i),
48749                 checked: !cm.isHidden(i),
48750                 hideOnClick:false
48751             }));
48752         }
48753     },
48754
48755     handleHdCtx : function(g, index, e){
48756         e.stopEvent();
48757         var hd = this.getHeaderCell(index);
48758         this.hdCtxIndex = index;
48759         var ms = this.hmenu.items, cm = this.cm;
48760         ms.get("asc").setDisabled(!cm.isSortable(index));
48761         ms.get("desc").setDisabled(!cm.isSortable(index));
48762         if(this.grid.enableColLock !== false){
48763             ms.get("lock").setDisabled(cm.isLocked(index));
48764             ms.get("unlock").setDisabled(!cm.isLocked(index));
48765         }
48766         this.hmenu.show(hd, "tl-bl");
48767     },
48768
48769     handleHdOver : function(e){
48770         var hd = this.findHeaderCell(e.getTarget());
48771         if(hd && !this.headersDisabled){
48772             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48773                this.fly(hd).addClass("x-grid-hd-over");
48774             }
48775         }
48776     },
48777
48778     handleHdOut : function(e){
48779         var hd = this.findHeaderCell(e.getTarget());
48780         if(hd){
48781             this.fly(hd).removeClass("x-grid-hd-over");
48782         }
48783     },
48784
48785     handleSplitDblClick : function(e, t){
48786         var i = this.getCellIndex(t);
48787         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48788             this.autoSizeColumn(i, true);
48789             this.layout();
48790         }
48791     },
48792
48793     render : function(){
48794
48795         var cm = this.cm;
48796         var colCount = cm.getColumnCount();
48797
48798         if(this.grid.monitorWindowResize === true){
48799             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48800         }
48801         var header = this.renderHeaders();
48802         var body = this.templates.body.apply({rows:""});
48803         var html = this.templates.master.apply({
48804             lockedBody: body,
48805             body: body,
48806             lockedHeader: header[0],
48807             header: header[1]
48808         });
48809
48810         //this.updateColumns();
48811
48812         this.grid.getGridEl().dom.innerHTML = html;
48813
48814         this.initElements();
48815         
48816         // a kludge to fix the random scolling effect in webkit
48817         this.el.on("scroll", function() {
48818             this.el.dom.scrollTop=0; // hopefully not recursive..
48819         },this);
48820
48821         this.scroller.on("scroll", this.handleScroll, this);
48822         this.lockedBody.on("mousewheel", this.handleWheel, this);
48823         this.mainBody.on("mousewheel", this.handleWheel, this);
48824
48825         this.mainHd.on("mouseover", this.handleHdOver, this);
48826         this.mainHd.on("mouseout", this.handleHdOut, this);
48827         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48828                 {delegate: "."+this.splitClass});
48829
48830         this.lockedHd.on("mouseover", this.handleHdOver, this);
48831         this.lockedHd.on("mouseout", this.handleHdOut, this);
48832         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48833                 {delegate: "."+this.splitClass});
48834
48835         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48836             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48837         }
48838
48839         this.updateSplitters();
48840
48841         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48842             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48843             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48844         }
48845
48846         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48847             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48848             this.hmenu.add(
48849                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48850                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48851             );
48852             if(this.grid.enableColLock !== false){
48853                 this.hmenu.add('-',
48854                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48855                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48856                 );
48857             }
48858             if(this.grid.enableColumnHide !== false){
48859
48860                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48861                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48862                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48863
48864                 this.hmenu.add('-',
48865                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48866                 );
48867             }
48868             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48869
48870             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48871         }
48872
48873         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48874             this.dd = new Roo.grid.GridDragZone(this.grid, {
48875                 ddGroup : this.grid.ddGroup || 'GridDD'
48876             });
48877         }
48878
48879         /*
48880         for(var i = 0; i < colCount; i++){
48881             if(cm.isHidden(i)){
48882                 this.hideColumn(i);
48883             }
48884             if(cm.config[i].align){
48885                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48886                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48887             }
48888         }*/
48889         
48890         this.updateHeaderSortState();
48891
48892         this.beforeInitialResize();
48893         this.layout(true);
48894
48895         // two part rendering gives faster view to the user
48896         this.renderPhase2.defer(1, this);
48897     },
48898
48899     renderPhase2 : function(){
48900         // render the rows now
48901         this.refresh();
48902         if(this.grid.autoSizeColumns){
48903             this.autoSizeColumns();
48904         }
48905     },
48906
48907     beforeInitialResize : function(){
48908
48909     },
48910
48911     onColumnSplitterMoved : function(i, w){
48912         this.userResized = true;
48913         var cm = this.grid.colModel;
48914         cm.setColumnWidth(i, w, true);
48915         var cid = cm.getColumnId(i);
48916         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48917         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48918         this.updateSplitters();
48919         this.layout();
48920         this.grid.fireEvent("columnresize", i, w);
48921     },
48922
48923     syncRowHeights : function(startIndex, endIndex){
48924         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48925             startIndex = startIndex || 0;
48926             var mrows = this.getBodyTable().rows;
48927             var lrows = this.getLockedTable().rows;
48928             var len = mrows.length-1;
48929             endIndex = Math.min(endIndex || len, len);
48930             for(var i = startIndex; i <= endIndex; i++){
48931                 var m = mrows[i], l = lrows[i];
48932                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48933                 m.style.height = l.style.height = h + "px";
48934             }
48935         }
48936     },
48937
48938     layout : function(initialRender, is2ndPass){
48939         var g = this.grid;
48940         var auto = g.autoHeight;
48941         var scrollOffset = 16;
48942         var c = g.getGridEl(), cm = this.cm,
48943                 expandCol = g.autoExpandColumn,
48944                 gv = this;
48945         //c.beginMeasure();
48946
48947         if(!c.dom.offsetWidth){ // display:none?
48948             if(initialRender){
48949                 this.lockedWrap.show();
48950                 this.mainWrap.show();
48951             }
48952             return;
48953         }
48954
48955         var hasLock = this.cm.isLocked(0);
48956
48957         var tbh = this.headerPanel.getHeight();
48958         var bbh = this.footerPanel.getHeight();
48959
48960         if(auto){
48961             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48962             var newHeight = ch + c.getBorderWidth("tb");
48963             if(g.maxHeight){
48964                 newHeight = Math.min(g.maxHeight, newHeight);
48965             }
48966             c.setHeight(newHeight);
48967         }
48968
48969         if(g.autoWidth){
48970             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48971         }
48972
48973         var s = this.scroller;
48974
48975         var csize = c.getSize(true);
48976
48977         this.el.setSize(csize.width, csize.height);
48978
48979         this.headerPanel.setWidth(csize.width);
48980         this.footerPanel.setWidth(csize.width);
48981
48982         var hdHeight = this.mainHd.getHeight();
48983         var vw = csize.width;
48984         var vh = csize.height - (tbh + bbh);
48985
48986         s.setSize(vw, vh);
48987
48988         var bt = this.getBodyTable();
48989         var ltWidth = hasLock ?
48990                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48991
48992         var scrollHeight = bt.offsetHeight;
48993         var scrollWidth = ltWidth + bt.offsetWidth;
48994         var vscroll = false, hscroll = false;
48995
48996         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48997
48998         var lw = this.lockedWrap, mw = this.mainWrap;
48999         var lb = this.lockedBody, mb = this.mainBody;
49000
49001         setTimeout(function(){
49002             var t = s.dom.offsetTop;
49003             var w = s.dom.clientWidth,
49004                 h = s.dom.clientHeight;
49005
49006             lw.setTop(t);
49007             lw.setSize(ltWidth, h);
49008
49009             mw.setLeftTop(ltWidth, t);
49010             mw.setSize(w-ltWidth, h);
49011
49012             lb.setHeight(h-hdHeight);
49013             mb.setHeight(h-hdHeight);
49014
49015             if(is2ndPass !== true && !gv.userResized && expandCol){
49016                 // high speed resize without full column calculation
49017                 
49018                 var ci = cm.getIndexById(expandCol);
49019                 if (ci < 0) {
49020                     ci = cm.findColumnIndex(expandCol);
49021                 }
49022                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49023                 var expandId = cm.getColumnId(ci);
49024                 var  tw = cm.getTotalWidth(false);
49025                 var currentWidth = cm.getColumnWidth(ci);
49026                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49027                 if(currentWidth != cw){
49028                     cm.setColumnWidth(ci, cw, true);
49029                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49030                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49031                     gv.updateSplitters();
49032                     gv.layout(false, true);
49033                 }
49034             }
49035
49036             if(initialRender){
49037                 lw.show();
49038                 mw.show();
49039             }
49040             //c.endMeasure();
49041         }, 10);
49042     },
49043
49044     onWindowResize : function(){
49045         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49046             return;
49047         }
49048         this.layout();
49049     },
49050
49051     appendFooter : function(parentEl){
49052         return null;
49053     },
49054
49055     sortAscText : "Sort Ascending",
49056     sortDescText : "Sort Descending",
49057     lockText : "Lock Column",
49058     unlockText : "Unlock Column",
49059     columnsText : "Columns"
49060 });
49061
49062
49063 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49064     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49065     this.proxy.el.addClass('x-grid3-col-dd');
49066 };
49067
49068 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49069     handleMouseDown : function(e){
49070
49071     },
49072
49073     callHandleMouseDown : function(e){
49074         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49075     }
49076 });
49077 /*
49078  * Based on:
49079  * Ext JS Library 1.1.1
49080  * Copyright(c) 2006-2007, Ext JS, LLC.
49081  *
49082  * Originally Released Under LGPL - original licence link has changed is not relivant.
49083  *
49084  * Fork - LGPL
49085  * <script type="text/javascript">
49086  */
49087  
49088 // private
49089 // This is a support class used internally by the Grid components
49090 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49091     this.grid = grid;
49092     this.view = grid.getView();
49093     this.proxy = this.view.resizeProxy;
49094     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49095         "gridSplitters" + this.grid.getGridEl().id, {
49096         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49097     });
49098     this.setHandleElId(Roo.id(hd));
49099     this.setOuterHandleElId(Roo.id(hd2));
49100     this.scroll = false;
49101 };
49102 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49103     fly: Roo.Element.fly,
49104
49105     b4StartDrag : function(x, y){
49106         this.view.headersDisabled = true;
49107         this.proxy.setHeight(this.view.mainWrap.getHeight());
49108         var w = this.cm.getColumnWidth(this.cellIndex);
49109         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49110         this.resetConstraints();
49111         this.setXConstraint(minw, 1000);
49112         this.setYConstraint(0, 0);
49113         this.minX = x - minw;
49114         this.maxX = x + 1000;
49115         this.startPos = x;
49116         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49117     },
49118
49119
49120     handleMouseDown : function(e){
49121         ev = Roo.EventObject.setEvent(e);
49122         var t = this.fly(ev.getTarget());
49123         if(t.hasClass("x-grid-split")){
49124             this.cellIndex = this.view.getCellIndex(t.dom);
49125             this.split = t.dom;
49126             this.cm = this.grid.colModel;
49127             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49128                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49129             }
49130         }
49131     },
49132
49133     endDrag : function(e){
49134         this.view.headersDisabled = false;
49135         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49136         var diff = endX - this.startPos;
49137         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49138     },
49139
49140     autoOffset : function(){
49141         this.setDelta(0,0);
49142     }
49143 });/*
49144  * Based on:
49145  * Ext JS Library 1.1.1
49146  * Copyright(c) 2006-2007, Ext JS, LLC.
49147  *
49148  * Originally Released Under LGPL - original licence link has changed is not relivant.
49149  *
49150  * Fork - LGPL
49151  * <script type="text/javascript">
49152  */
49153  
49154 // private
49155 // This is a support class used internally by the Grid components
49156 Roo.grid.GridDragZone = function(grid, config){
49157     this.view = grid.getView();
49158     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49159     if(this.view.lockedBody){
49160         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49161         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49162     }
49163     this.scroll = false;
49164     this.grid = grid;
49165     this.ddel = document.createElement('div');
49166     this.ddel.className = 'x-grid-dd-wrap';
49167 };
49168
49169 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49170     ddGroup : "GridDD",
49171
49172     getDragData : function(e){
49173         var t = Roo.lib.Event.getTarget(e);
49174         var rowIndex = this.view.findRowIndex(t);
49175         if(rowIndex !== false){
49176             var sm = this.grid.selModel;
49177             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49178               //  sm.mouseDown(e, t);
49179             //}
49180             if (e.hasModifier()){
49181                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49182             }
49183             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49184         }
49185         return false;
49186     },
49187
49188     onInitDrag : function(e){
49189         var data = this.dragData;
49190         this.ddel.innerHTML = this.grid.getDragDropText();
49191         this.proxy.update(this.ddel);
49192         // fire start drag?
49193     },
49194
49195     afterRepair : function(){
49196         this.dragging = false;
49197     },
49198
49199     getRepairXY : function(e, data){
49200         return false;
49201     },
49202
49203     onEndDrag : function(data, e){
49204         // fire end drag?
49205     },
49206
49207     onValidDrop : function(dd, e, id){
49208         // fire drag drop?
49209         this.hideProxy();
49210     },
49211
49212     beforeInvalidDrop : function(e, id){
49213
49214     }
49215 });/*
49216  * Based on:
49217  * Ext JS Library 1.1.1
49218  * Copyright(c) 2006-2007, Ext JS, LLC.
49219  *
49220  * Originally Released Under LGPL - original licence link has changed is not relivant.
49221  *
49222  * Fork - LGPL
49223  * <script type="text/javascript">
49224  */
49225  
49226
49227 /**
49228  * @class Roo.grid.ColumnModel
49229  * @extends Roo.util.Observable
49230  * This is the default implementation of a ColumnModel used by the Grid. It defines
49231  * the columns in the grid.
49232  * <br>Usage:<br>
49233  <pre><code>
49234  var colModel = new Roo.grid.ColumnModel([
49235         {header: "Ticker", width: 60, sortable: true, locked: true},
49236         {header: "Company Name", width: 150, sortable: true},
49237         {header: "Market Cap.", width: 100, sortable: true},
49238         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49239         {header: "Employees", width: 100, sortable: true, resizable: false}
49240  ]);
49241  </code></pre>
49242  * <p>
49243  
49244  * The config options listed for this class are options which may appear in each
49245  * individual column definition.
49246  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49247  * @constructor
49248  * @param {Object} config An Array of column config objects. See this class's
49249  * config objects for details.
49250 */
49251 Roo.grid.ColumnModel = function(config){
49252         /**
49253      * The config passed into the constructor
49254      */
49255     this.config = config;
49256     this.lookup = {};
49257
49258     // if no id, create one
49259     // if the column does not have a dataIndex mapping,
49260     // map it to the order it is in the config
49261     for(var i = 0, len = config.length; i < len; i++){
49262         var c = config[i];
49263         if(typeof c.dataIndex == "undefined"){
49264             c.dataIndex = i;
49265         }
49266         if(typeof c.renderer == "string"){
49267             c.renderer = Roo.util.Format[c.renderer];
49268         }
49269         if(typeof c.id == "undefined"){
49270             c.id = Roo.id();
49271         }
49272         if(c.editor && c.editor.xtype){
49273             c.editor  = Roo.factory(c.editor, Roo.grid);
49274         }
49275         if(c.editor && c.editor.isFormField){
49276             c.editor = new Roo.grid.GridEditor(c.editor);
49277         }
49278         this.lookup[c.id] = c;
49279     }
49280
49281     /**
49282      * The width of columns which have no width specified (defaults to 100)
49283      * @type Number
49284      */
49285     this.defaultWidth = 100;
49286
49287     /**
49288      * Default sortable of columns which have no sortable specified (defaults to false)
49289      * @type Boolean
49290      */
49291     this.defaultSortable = false;
49292
49293     this.addEvents({
49294         /**
49295              * @event widthchange
49296              * Fires when the width of a column changes.
49297              * @param {ColumnModel} this
49298              * @param {Number} columnIndex The column index
49299              * @param {Number} newWidth The new width
49300              */
49301             "widthchange": true,
49302         /**
49303              * @event headerchange
49304              * Fires when the text of a header changes.
49305              * @param {ColumnModel} this
49306              * @param {Number} columnIndex The column index
49307              * @param {Number} newText The new header text
49308              */
49309             "headerchange": true,
49310         /**
49311              * @event hiddenchange
49312              * Fires when a column is hidden or "unhidden".
49313              * @param {ColumnModel} this
49314              * @param {Number} columnIndex The column index
49315              * @param {Boolean} hidden true if hidden, false otherwise
49316              */
49317             "hiddenchange": true,
49318             /**
49319          * @event columnmoved
49320          * Fires when a column is moved.
49321          * @param {ColumnModel} this
49322          * @param {Number} oldIndex
49323          * @param {Number} newIndex
49324          */
49325         "columnmoved" : true,
49326         /**
49327          * @event columlockchange
49328          * Fires when a column's locked state is changed
49329          * @param {ColumnModel} this
49330          * @param {Number} colIndex
49331          * @param {Boolean} locked true if locked
49332          */
49333         "columnlockchange" : true
49334     });
49335     Roo.grid.ColumnModel.superclass.constructor.call(this);
49336 };
49337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49338     /**
49339      * @cfg {String} header The header text to display in the Grid view.
49340      */
49341     /**
49342      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49343      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49344      * specified, the column's index is used as an index into the Record's data Array.
49345      */
49346     /**
49347      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49348      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49349      */
49350     /**
49351      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49352      * Defaults to the value of the {@link #defaultSortable} property.
49353      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49354      */
49355     /**
49356      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49357      */
49358     /**
49359      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49360      */
49361     /**
49362      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49363      */
49364     /**
49365      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49366      */
49367     /**
49368      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49369      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49370      * default renderer uses the raw data value.
49371      */
49372        /**
49373      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49374      */
49375     /**
49376      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49377      */
49378
49379     /**
49380      * Returns the id of the column at the specified index.
49381      * @param {Number} index The column index
49382      * @return {String} the id
49383      */
49384     getColumnId : function(index){
49385         return this.config[index].id;
49386     },
49387
49388     /**
49389      * Returns the column for a specified id.
49390      * @param {String} id The column id
49391      * @return {Object} the column
49392      */
49393     getColumnById : function(id){
49394         return this.lookup[id];
49395     },
49396
49397     
49398     /**
49399      * Returns the column for a specified dataIndex.
49400      * @param {String} dataIndex The column dataIndex
49401      * @return {Object|Boolean} the column or false if not found
49402      */
49403     getColumnByDataIndex: function(dataIndex){
49404         var index = this.findColumnIndex(dataIndex);
49405         return index > -1 ? this.config[index] : false;
49406     },
49407     
49408     /**
49409      * Returns the index for a specified column id.
49410      * @param {String} id The column id
49411      * @return {Number} the index, or -1 if not found
49412      */
49413     getIndexById : function(id){
49414         for(var i = 0, len = this.config.length; i < len; i++){
49415             if(this.config[i].id == id){
49416                 return i;
49417             }
49418         }
49419         return -1;
49420     },
49421     
49422     /**
49423      * Returns the index for a specified column dataIndex.
49424      * @param {String} dataIndex The column dataIndex
49425      * @return {Number} the index, or -1 if not found
49426      */
49427     
49428     findColumnIndex : function(dataIndex){
49429         for(var i = 0, len = this.config.length; i < len; i++){
49430             if(this.config[i].dataIndex == dataIndex){
49431                 return i;
49432             }
49433         }
49434         return -1;
49435     },
49436     
49437     
49438     moveColumn : function(oldIndex, newIndex){
49439         var c = this.config[oldIndex];
49440         this.config.splice(oldIndex, 1);
49441         this.config.splice(newIndex, 0, c);
49442         this.dataMap = null;
49443         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49444     },
49445
49446     isLocked : function(colIndex){
49447         return this.config[colIndex].locked === true;
49448     },
49449
49450     setLocked : function(colIndex, value, suppressEvent){
49451         if(this.isLocked(colIndex) == value){
49452             return;
49453         }
49454         this.config[colIndex].locked = value;
49455         if(!suppressEvent){
49456             this.fireEvent("columnlockchange", this, colIndex, value);
49457         }
49458     },
49459
49460     getTotalLockedWidth : function(){
49461         var totalWidth = 0;
49462         for(var i = 0; i < this.config.length; i++){
49463             if(this.isLocked(i) && !this.isHidden(i)){
49464                 this.totalWidth += this.getColumnWidth(i);
49465             }
49466         }
49467         return totalWidth;
49468     },
49469
49470     getLockedCount : function(){
49471         for(var i = 0, len = this.config.length; i < len; i++){
49472             if(!this.isLocked(i)){
49473                 return i;
49474             }
49475         }
49476     },
49477
49478     /**
49479      * Returns the number of columns.
49480      * @return {Number}
49481      */
49482     getColumnCount : function(visibleOnly){
49483         if(visibleOnly === true){
49484             var c = 0;
49485             for(var i = 0, len = this.config.length; i < len; i++){
49486                 if(!this.isHidden(i)){
49487                     c++;
49488                 }
49489             }
49490             return c;
49491         }
49492         return this.config.length;
49493     },
49494
49495     /**
49496      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49497      * @param {Function} fn
49498      * @param {Object} scope (optional)
49499      * @return {Array} result
49500      */
49501     getColumnsBy : function(fn, scope){
49502         var r = [];
49503         for(var i = 0, len = this.config.length; i < len; i++){
49504             var c = this.config[i];
49505             if(fn.call(scope||this, c, i) === true){
49506                 r[r.length] = c;
49507             }
49508         }
49509         return r;
49510     },
49511
49512     /**
49513      * Returns true if the specified column is sortable.
49514      * @param {Number} col The column index
49515      * @return {Boolean}
49516      */
49517     isSortable : function(col){
49518         if(typeof this.config[col].sortable == "undefined"){
49519             return this.defaultSortable;
49520         }
49521         return this.config[col].sortable;
49522     },
49523
49524     /**
49525      * Returns the rendering (formatting) function defined for the column.
49526      * @param {Number} col The column index.
49527      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49528      */
49529     getRenderer : function(col){
49530         if(!this.config[col].renderer){
49531             return Roo.grid.ColumnModel.defaultRenderer;
49532         }
49533         return this.config[col].renderer;
49534     },
49535
49536     /**
49537      * Sets the rendering (formatting) function for a column.
49538      * @param {Number} col The column index
49539      * @param {Function} fn The function to use to process the cell's raw data
49540      * to return HTML markup for the grid view. The render function is called with
49541      * the following parameters:<ul>
49542      * <li>Data value.</li>
49543      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49544      * <li>css A CSS style string to apply to the table cell.</li>
49545      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49546      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49547      * <li>Row index</li>
49548      * <li>Column index</li>
49549      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49550      */
49551     setRenderer : function(col, fn){
49552         this.config[col].renderer = fn;
49553     },
49554
49555     /**
49556      * Returns the width for the specified column.
49557      * @param {Number} col The column index
49558      * @return {Number}
49559      */
49560     getColumnWidth : function(col){
49561         return this.config[col].width * 1 || this.defaultWidth;
49562     },
49563
49564     /**
49565      * Sets the width for a column.
49566      * @param {Number} col The column index
49567      * @param {Number} width The new width
49568      */
49569     setColumnWidth : function(col, width, suppressEvent){
49570         this.config[col].width = width;
49571         this.totalWidth = null;
49572         if(!suppressEvent){
49573              this.fireEvent("widthchange", this, col, width);
49574         }
49575     },
49576
49577     /**
49578      * Returns the total width of all columns.
49579      * @param {Boolean} includeHidden True to include hidden column widths
49580      * @return {Number}
49581      */
49582     getTotalWidth : function(includeHidden){
49583         if(!this.totalWidth){
49584             this.totalWidth = 0;
49585             for(var i = 0, len = this.config.length; i < len; i++){
49586                 if(includeHidden || !this.isHidden(i)){
49587                     this.totalWidth += this.getColumnWidth(i);
49588                 }
49589             }
49590         }
49591         return this.totalWidth;
49592     },
49593
49594     /**
49595      * Returns the header for the specified column.
49596      * @param {Number} col The column index
49597      * @return {String}
49598      */
49599     getColumnHeader : function(col){
49600         return this.config[col].header;
49601     },
49602
49603     /**
49604      * Sets the header for a column.
49605      * @param {Number} col The column index
49606      * @param {String} header The new header
49607      */
49608     setColumnHeader : function(col, header){
49609         this.config[col].header = header;
49610         this.fireEvent("headerchange", this, col, header);
49611     },
49612
49613     /**
49614      * Returns the tooltip for the specified column.
49615      * @param {Number} col The column index
49616      * @return {String}
49617      */
49618     getColumnTooltip : function(col){
49619             return this.config[col].tooltip;
49620     },
49621     /**
49622      * Sets the tooltip for a column.
49623      * @param {Number} col The column index
49624      * @param {String} tooltip The new tooltip
49625      */
49626     setColumnTooltip : function(col, tooltip){
49627             this.config[col].tooltip = tooltip;
49628     },
49629
49630     /**
49631      * Returns the dataIndex for the specified column.
49632      * @param {Number} col The column index
49633      * @return {Number}
49634      */
49635     getDataIndex : function(col){
49636         return this.config[col].dataIndex;
49637     },
49638
49639     /**
49640      * Sets the dataIndex for a column.
49641      * @param {Number} col The column index
49642      * @param {Number} dataIndex The new dataIndex
49643      */
49644     setDataIndex : function(col, dataIndex){
49645         this.config[col].dataIndex = dataIndex;
49646     },
49647
49648     
49649     
49650     /**
49651      * Returns true if the cell is editable.
49652      * @param {Number} colIndex The column index
49653      * @param {Number} rowIndex The row index
49654      * @return {Boolean}
49655      */
49656     isCellEditable : function(colIndex, rowIndex){
49657         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49658     },
49659
49660     /**
49661      * Returns the editor defined for the cell/column.
49662      * return false or null to disable editing.
49663      * @param {Number} colIndex The column index
49664      * @param {Number} rowIndex The row index
49665      * @return {Object}
49666      */
49667     getCellEditor : function(colIndex, rowIndex){
49668         return this.config[colIndex].editor;
49669     },
49670
49671     /**
49672      * Sets if a column is editable.
49673      * @param {Number} col The column index
49674      * @param {Boolean} editable True if the column is editable
49675      */
49676     setEditable : function(col, editable){
49677         this.config[col].editable = editable;
49678     },
49679
49680
49681     /**
49682      * Returns true if the column is hidden.
49683      * @param {Number} colIndex The column index
49684      * @return {Boolean}
49685      */
49686     isHidden : function(colIndex){
49687         return this.config[colIndex].hidden;
49688     },
49689
49690
49691     /**
49692      * Returns true if the column width cannot be changed
49693      */
49694     isFixed : function(colIndex){
49695         return this.config[colIndex].fixed;
49696     },
49697
49698     /**
49699      * Returns true if the column can be resized
49700      * @return {Boolean}
49701      */
49702     isResizable : function(colIndex){
49703         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49704     },
49705     /**
49706      * Sets if a column is hidden.
49707      * @param {Number} colIndex The column index
49708      * @param {Boolean} hidden True if the column is hidden
49709      */
49710     setHidden : function(colIndex, hidden){
49711         this.config[colIndex].hidden = hidden;
49712         this.totalWidth = null;
49713         this.fireEvent("hiddenchange", this, colIndex, hidden);
49714     },
49715
49716     /**
49717      * Sets the editor for a column.
49718      * @param {Number} col The column index
49719      * @param {Object} editor The editor object
49720      */
49721     setEditor : function(col, editor){
49722         this.config[col].editor = editor;
49723     }
49724 });
49725
49726 Roo.grid.ColumnModel.defaultRenderer = function(value){
49727         if(typeof value == "string" && value.length < 1){
49728             return "&#160;";
49729         }
49730         return value;
49731 };
49732
49733 // Alias for backwards compatibility
49734 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49735 /*
49736  * Based on:
49737  * Ext JS Library 1.1.1
49738  * Copyright(c) 2006-2007, Ext JS, LLC.
49739  *
49740  * Originally Released Under LGPL - original licence link has changed is not relivant.
49741  *
49742  * Fork - LGPL
49743  * <script type="text/javascript">
49744  */
49745
49746 /**
49747  * @class Roo.grid.AbstractSelectionModel
49748  * @extends Roo.util.Observable
49749  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49750  * implemented by descendant classes.  This class should not be directly instantiated.
49751  * @constructor
49752  */
49753 Roo.grid.AbstractSelectionModel = function(){
49754     this.locked = false;
49755     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49756 };
49757
49758 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49759     /** @ignore Called by the grid automatically. Do not call directly. */
49760     init : function(grid){
49761         this.grid = grid;
49762         this.initEvents();
49763     },
49764
49765     /**
49766      * Locks the selections.
49767      */
49768     lock : function(){
49769         this.locked = true;
49770     },
49771
49772     /**
49773      * Unlocks the selections.
49774      */
49775     unlock : function(){
49776         this.locked = false;
49777     },
49778
49779     /**
49780      * Returns true if the selections are locked.
49781      * @return {Boolean}
49782      */
49783     isLocked : function(){
49784         return this.locked;
49785     }
49786 });/*
49787  * Based on:
49788  * Ext JS Library 1.1.1
49789  * Copyright(c) 2006-2007, Ext JS, LLC.
49790  *
49791  * Originally Released Under LGPL - original licence link has changed is not relivant.
49792  *
49793  * Fork - LGPL
49794  * <script type="text/javascript">
49795  */
49796 /**
49797  * @extends Roo.grid.AbstractSelectionModel
49798  * @class Roo.grid.RowSelectionModel
49799  * The default SelectionModel used by {@link Roo.grid.Grid}.
49800  * It supports multiple selections and keyboard selection/navigation. 
49801  * @constructor
49802  * @param {Object} config
49803  */
49804 Roo.grid.RowSelectionModel = function(config){
49805     Roo.apply(this, config);
49806     this.selections = new Roo.util.MixedCollection(false, function(o){
49807         return o.id;
49808     });
49809
49810     this.last = false;
49811     this.lastActive = false;
49812
49813     this.addEvents({
49814         /**
49815              * @event selectionchange
49816              * Fires when the selection changes
49817              * @param {SelectionModel} this
49818              */
49819             "selectionchange" : true,
49820         /**
49821              * @event afterselectionchange
49822              * Fires after the selection changes (eg. by key press or clicking)
49823              * @param {SelectionModel} this
49824              */
49825             "afterselectionchange" : true,
49826         /**
49827              * @event beforerowselect
49828              * Fires when a row is selected being selected, return false to cancel.
49829              * @param {SelectionModel} this
49830              * @param {Number} rowIndex The selected index
49831              * @param {Boolean} keepExisting False if other selections will be cleared
49832              */
49833             "beforerowselect" : true,
49834         /**
49835              * @event rowselect
49836              * Fires when a row is selected.
49837              * @param {SelectionModel} this
49838              * @param {Number} rowIndex The selected index
49839              * @param {Roo.data.Record} r The record
49840              */
49841             "rowselect" : true,
49842         /**
49843              * @event rowdeselect
49844              * Fires when a row is deselected.
49845              * @param {SelectionModel} this
49846              * @param {Number} rowIndex The selected index
49847              */
49848         "rowdeselect" : true
49849     });
49850     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49851     this.locked = false;
49852 };
49853
49854 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49855     /**
49856      * @cfg {Boolean} singleSelect
49857      * True to allow selection of only one row at a time (defaults to false)
49858      */
49859     singleSelect : false,
49860
49861     // private
49862     initEvents : function(){
49863
49864         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49865             this.grid.on("mousedown", this.handleMouseDown, this);
49866         }else{ // allow click to work like normal
49867             this.grid.on("rowclick", this.handleDragableRowClick, this);
49868         }
49869
49870         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49871             "up" : function(e){
49872                 if(!e.shiftKey){
49873                     this.selectPrevious(e.shiftKey);
49874                 }else if(this.last !== false && this.lastActive !== false){
49875                     var last = this.last;
49876                     this.selectRange(this.last,  this.lastActive-1);
49877                     this.grid.getView().focusRow(this.lastActive);
49878                     if(last !== false){
49879                         this.last = last;
49880                     }
49881                 }else{
49882                     this.selectFirstRow();
49883                 }
49884                 this.fireEvent("afterselectionchange", this);
49885             },
49886             "down" : function(e){
49887                 if(!e.shiftKey){
49888                     this.selectNext(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             scope: this
49902         });
49903
49904         var view = this.grid.view;
49905         view.on("refresh", this.onRefresh, this);
49906         view.on("rowupdated", this.onRowUpdated, this);
49907         view.on("rowremoved", this.onRemove, this);
49908     },
49909
49910     // private
49911     onRefresh : function(){
49912         var ds = this.grid.dataSource, i, v = this.grid.view;
49913         var s = this.selections;
49914         s.each(function(r){
49915             if((i = ds.indexOfId(r.id)) != -1){
49916                 v.onRowSelect(i);
49917             }else{
49918                 s.remove(r);
49919             }
49920         });
49921     },
49922
49923     // private
49924     onRemove : function(v, index, r){
49925         this.selections.remove(r);
49926     },
49927
49928     // private
49929     onRowUpdated : function(v, index, r){
49930         if(this.isSelected(r)){
49931             v.onRowSelect(index);
49932         }
49933     },
49934
49935     /**
49936      * Select records.
49937      * @param {Array} records The records to select
49938      * @param {Boolean} keepExisting (optional) True to keep existing selections
49939      */
49940     selectRecords : function(records, keepExisting){
49941         if(!keepExisting){
49942             this.clearSelections();
49943         }
49944         var ds = this.grid.dataSource;
49945         for(var i = 0, len = records.length; i < len; i++){
49946             this.selectRow(ds.indexOf(records[i]), true);
49947         }
49948     },
49949
49950     /**
49951      * Gets the number of selected rows.
49952      * @return {Number}
49953      */
49954     getCount : function(){
49955         return this.selections.length;
49956     },
49957
49958     /**
49959      * Selects the first row in the grid.
49960      */
49961     selectFirstRow : function(){
49962         this.selectRow(0);
49963     },
49964
49965     /**
49966      * Select the last row.
49967      * @param {Boolean} keepExisting (optional) True to keep existing selections
49968      */
49969     selectLastRow : function(keepExisting){
49970         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49971     },
49972
49973     /**
49974      * Selects the row immediately following the last selected row.
49975      * @param {Boolean} keepExisting (optional) True to keep existing selections
49976      */
49977     selectNext : function(keepExisting){
49978         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49979             this.selectRow(this.last+1, keepExisting);
49980             this.grid.getView().focusRow(this.last);
49981         }
49982     },
49983
49984     /**
49985      * Selects the row that precedes the last selected row.
49986      * @param {Boolean} keepExisting (optional) True to keep existing selections
49987      */
49988     selectPrevious : function(keepExisting){
49989         if(this.last){
49990             this.selectRow(this.last-1, keepExisting);
49991             this.grid.getView().focusRow(this.last);
49992         }
49993     },
49994
49995     /**
49996      * Returns the selected records
49997      * @return {Array} Array of selected records
49998      */
49999     getSelections : function(){
50000         return [].concat(this.selections.items);
50001     },
50002
50003     /**
50004      * Returns the first selected record.
50005      * @return {Record}
50006      */
50007     getSelected : function(){
50008         return this.selections.itemAt(0);
50009     },
50010
50011
50012     /**
50013      * Clears all selections.
50014      */
50015     clearSelections : function(fast){
50016         if(this.locked) return;
50017         if(fast !== true){
50018             var ds = this.grid.dataSource;
50019             var s = this.selections;
50020             s.each(function(r){
50021                 this.deselectRow(ds.indexOfId(r.id));
50022             }, this);
50023             s.clear();
50024         }else{
50025             this.selections.clear();
50026         }
50027         this.last = false;
50028     },
50029
50030
50031     /**
50032      * Selects all rows.
50033      */
50034     selectAll : function(){
50035         if(this.locked) return;
50036         this.selections.clear();
50037         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50038             this.selectRow(i, true);
50039         }
50040     },
50041
50042     /**
50043      * Returns True if there is a selection.
50044      * @return {Boolean}
50045      */
50046     hasSelection : function(){
50047         return this.selections.length > 0;
50048     },
50049
50050     /**
50051      * Returns True if the specified row is selected.
50052      * @param {Number/Record} record The record or index of the record to check
50053      * @return {Boolean}
50054      */
50055     isSelected : function(index){
50056         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50057         return (r && this.selections.key(r.id) ? true : false);
50058     },
50059
50060     /**
50061      * Returns True if the specified record id is selected.
50062      * @param {String} id The id of record to check
50063      * @return {Boolean}
50064      */
50065     isIdSelected : function(id){
50066         return (this.selections.key(id) ? true : false);
50067     },
50068
50069     // private
50070     handleMouseDown : function(e, t){
50071         var view = this.grid.getView(), rowIndex;
50072         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50073             return;
50074         };
50075         if(e.shiftKey && this.last !== false){
50076             var last = this.last;
50077             this.selectRange(last, rowIndex, e.ctrlKey);
50078             this.last = last; // reset the last
50079             view.focusRow(rowIndex);
50080         }else{
50081             var isSelected = this.isSelected(rowIndex);
50082             if(e.button !== 0 && isSelected){
50083                 view.focusRow(rowIndex);
50084             }else if(e.ctrlKey && isSelected){
50085                 this.deselectRow(rowIndex);
50086             }else if(!isSelected){
50087                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50088                 view.focusRow(rowIndex);
50089             }
50090         }
50091         this.fireEvent("afterselectionchange", this);
50092     },
50093     // private
50094     handleDragableRowClick :  function(grid, rowIndex, e) 
50095     {
50096         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50097             this.selectRow(rowIndex, false);
50098             grid.view.focusRow(rowIndex);
50099              this.fireEvent("afterselectionchange", this);
50100         }
50101     },
50102     
50103     /**
50104      * Selects multiple rows.
50105      * @param {Array} rows Array of the indexes of the row to select
50106      * @param {Boolean} keepExisting (optional) True to keep existing selections
50107      */
50108     selectRows : function(rows, keepExisting){
50109         if(!keepExisting){
50110             this.clearSelections();
50111         }
50112         for(var i = 0, len = rows.length; i < len; i++){
50113             this.selectRow(rows[i], true);
50114         }
50115     },
50116
50117     /**
50118      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50119      * @param {Number} startRow The index of the first row in the range
50120      * @param {Number} endRow The index of the last row in the range
50121      * @param {Boolean} keepExisting (optional) True to retain existing selections
50122      */
50123     selectRange : function(startRow, endRow, keepExisting){
50124         if(this.locked) return;
50125         if(!keepExisting){
50126             this.clearSelections();
50127         }
50128         if(startRow <= endRow){
50129             for(var i = startRow; i <= endRow; i++){
50130                 this.selectRow(i, true);
50131             }
50132         }else{
50133             for(var i = startRow; i >= endRow; i--){
50134                 this.selectRow(i, true);
50135             }
50136         }
50137     },
50138
50139     /**
50140      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50141      * @param {Number} startRow The index of the first row in the range
50142      * @param {Number} endRow The index of the last row in the range
50143      */
50144     deselectRange : function(startRow, endRow, preventViewNotify){
50145         if(this.locked) return;
50146         for(var i = startRow; i <= endRow; i++){
50147             this.deselectRow(i, preventViewNotify);
50148         }
50149     },
50150
50151     /**
50152      * Selects a row.
50153      * @param {Number} row The index of the row to select
50154      * @param {Boolean} keepExisting (optional) True to keep existing selections
50155      */
50156     selectRow : function(index, keepExisting, preventViewNotify){
50157         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50158         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50159             if(!keepExisting || this.singleSelect){
50160                 this.clearSelections();
50161             }
50162             var r = this.grid.dataSource.getAt(index);
50163             this.selections.add(r);
50164             this.last = this.lastActive = index;
50165             if(!preventViewNotify){
50166                 this.grid.getView().onRowSelect(index);
50167             }
50168             this.fireEvent("rowselect", this, index, r);
50169             this.fireEvent("selectionchange", this);
50170         }
50171     },
50172
50173     /**
50174      * Deselects a row.
50175      * @param {Number} row The index of the row to deselect
50176      */
50177     deselectRow : function(index, preventViewNotify){
50178         if(this.locked) return;
50179         if(this.last == index){
50180             this.last = false;
50181         }
50182         if(this.lastActive == index){
50183             this.lastActive = false;
50184         }
50185         var r = this.grid.dataSource.getAt(index);
50186         this.selections.remove(r);
50187         if(!preventViewNotify){
50188             this.grid.getView().onRowDeselect(index);
50189         }
50190         this.fireEvent("rowdeselect", this, index);
50191         this.fireEvent("selectionchange", this);
50192     },
50193
50194     // private
50195     restoreLast : function(){
50196         if(this._last){
50197             this.last = this._last;
50198         }
50199     },
50200
50201     // private
50202     acceptsNav : function(row, col, cm){
50203         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50204     },
50205
50206     // private
50207     onEditorKey : function(field, e){
50208         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50209         if(k == e.TAB){
50210             e.stopEvent();
50211             ed.completeEdit();
50212             if(e.shiftKey){
50213                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50214             }else{
50215                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50216             }
50217         }else if(k == e.ENTER && !e.ctrlKey){
50218             e.stopEvent();
50219             ed.completeEdit();
50220             if(e.shiftKey){
50221                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50222             }else{
50223                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50224             }
50225         }else if(k == e.ESC){
50226             ed.cancelEdit();
50227         }
50228         if(newCell){
50229             g.startEditing(newCell[0], newCell[1]);
50230         }
50231     }
50232 });/*
50233  * Based on:
50234  * Ext JS Library 1.1.1
50235  * Copyright(c) 2006-2007, Ext JS, LLC.
50236  *
50237  * Originally Released Under LGPL - original licence link has changed is not relivant.
50238  *
50239  * Fork - LGPL
50240  * <script type="text/javascript">
50241  */
50242 /**
50243  * @class Roo.grid.CellSelectionModel
50244  * @extends Roo.grid.AbstractSelectionModel
50245  * This class provides the basic implementation for cell selection in a grid.
50246  * @constructor
50247  * @param {Object} config The object containing the configuration of this model.
50248  */
50249 Roo.grid.CellSelectionModel = function(config){
50250     Roo.apply(this, config);
50251
50252     this.selection = null;
50253
50254     this.addEvents({
50255         /**
50256              * @event beforerowselect
50257              * Fires before a cell is selected.
50258              * @param {SelectionModel} this
50259              * @param {Number} rowIndex The selected row index
50260              * @param {Number} colIndex The selected cell index
50261              */
50262             "beforecellselect" : true,
50263         /**
50264              * @event cellselect
50265              * Fires when a cell is selected.
50266              * @param {SelectionModel} this
50267              * @param {Number} rowIndex The selected row index
50268              * @param {Number} colIndex The selected cell index
50269              */
50270             "cellselect" : true,
50271         /**
50272              * @event selectionchange
50273              * Fires when the active selection changes.
50274              * @param {SelectionModel} this
50275              * @param {Object} selection null for no selection or an object (o) with two properties
50276                 <ul>
50277                 <li>o.record: the record object for the row the selection is in</li>
50278                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50279                 </ul>
50280              */
50281             "selectionchange" : true
50282     });
50283     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50284 };
50285
50286 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50287
50288     /** @ignore */
50289     initEvents : function(){
50290         this.grid.on("mousedown", this.handleMouseDown, this);
50291         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50292         var view = this.grid.view;
50293         view.on("refresh", this.onViewChange, this);
50294         view.on("rowupdated", this.onRowUpdated, this);
50295         view.on("beforerowremoved", this.clearSelections, this);
50296         view.on("beforerowsinserted", this.clearSelections, this);
50297         if(this.grid.isEditor){
50298             this.grid.on("beforeedit", this.beforeEdit,  this);
50299         }
50300     },
50301
50302         //private
50303     beforeEdit : function(e){
50304         this.select(e.row, e.column, false, true, e.record);
50305     },
50306
50307         //private
50308     onRowUpdated : function(v, index, r){
50309         if(this.selection && this.selection.record == r){
50310             v.onCellSelect(index, this.selection.cell[1]);
50311         }
50312     },
50313
50314         //private
50315     onViewChange : function(){
50316         this.clearSelections(true);
50317     },
50318
50319         /**
50320          * Returns the currently selected cell,.
50321          * @return {Array} The selected cell (row, column) or null if none selected.
50322          */
50323     getSelectedCell : function(){
50324         return this.selection ? this.selection.cell : null;
50325     },
50326
50327     /**
50328      * Clears all selections.
50329      * @param {Boolean} true to prevent the gridview from being notified about the change.
50330      */
50331     clearSelections : function(preventNotify){
50332         var s = this.selection;
50333         if(s){
50334             if(preventNotify !== true){
50335                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50336             }
50337             this.selection = null;
50338             this.fireEvent("selectionchange", this, null);
50339         }
50340     },
50341
50342     /**
50343      * Returns true if there is a selection.
50344      * @return {Boolean}
50345      */
50346     hasSelection : function(){
50347         return this.selection ? true : false;
50348     },
50349
50350     /** @ignore */
50351     handleMouseDown : function(e, t){
50352         var v = this.grid.getView();
50353         if(this.isLocked()){
50354             return;
50355         };
50356         var row = v.findRowIndex(t);
50357         var cell = v.findCellIndex(t);
50358         if(row !== false && cell !== false){
50359             this.select(row, cell);
50360         }
50361     },
50362
50363     /**
50364      * Selects a cell.
50365      * @param {Number} rowIndex
50366      * @param {Number} collIndex
50367      */
50368     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50369         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50370             this.clearSelections();
50371             r = r || this.grid.dataSource.getAt(rowIndex);
50372             this.selection = {
50373                 record : r,
50374                 cell : [rowIndex, colIndex]
50375             };
50376             if(!preventViewNotify){
50377                 var v = this.grid.getView();
50378                 v.onCellSelect(rowIndex, colIndex);
50379                 if(preventFocus !== true){
50380                     v.focusCell(rowIndex, colIndex);
50381                 }
50382             }
50383             this.fireEvent("cellselect", this, rowIndex, colIndex);
50384             this.fireEvent("selectionchange", this, this.selection);
50385         }
50386     },
50387
50388         //private
50389     isSelectable : function(rowIndex, colIndex, cm){
50390         return !cm.isHidden(colIndex);
50391     },
50392
50393     /** @ignore */
50394     handleKeyDown : function(e){
50395         Roo.log('Cell Sel Model handleKeyDown');
50396         if(!e.isNavKeyPress()){
50397             return;
50398         }
50399         var g = this.grid, s = this.selection;
50400         if(!s){
50401             e.stopEvent();
50402             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50403             if(cell){
50404                 this.select(cell[0], cell[1]);
50405             }
50406             return;
50407         }
50408         var sm = this;
50409         var walk = function(row, col, step){
50410             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50411         };
50412         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50413         var newCell;
50414
50415         switch(k){
50416             case e.TAB:
50417                 // handled by onEditorKey
50418                 if (g.isEditor && g.editing) {
50419                     return;
50420                 }
50421                 if(e.shiftKey){
50422                      newCell = walk(r, c-1, -1);
50423                 }else{
50424                      newCell = walk(r, c+1, 1);
50425                 }
50426              break;
50427              case e.DOWN:
50428                  newCell = walk(r+1, c, 1);
50429              break;
50430              case e.UP:
50431                  newCell = walk(r-1, c, -1);
50432              break;
50433              case e.RIGHT:
50434                  newCell = walk(r, c+1, 1);
50435              break;
50436              case e.LEFT:
50437                  newCell = walk(r, c-1, -1);
50438              break;
50439              case e.ENTER:
50440                  if(g.isEditor && !g.editing){
50441                     g.startEditing(r, c);
50442                     e.stopEvent();
50443                     return;
50444                 }
50445              break;
50446         };
50447         if(newCell){
50448             this.select(newCell[0], newCell[1]);
50449             e.stopEvent();
50450         }
50451     },
50452
50453     acceptsNav : function(row, col, cm){
50454         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50455     },
50456
50457     onEditorKey : function(field, e){
50458         
50459         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50460         ///Roo.log('onEditorKey' + k);
50461         
50462         if(k == e.TAB){
50463             if(e.shiftKey){
50464                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50465             }else{
50466                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50467             }
50468             e.stopEvent();
50469         }else if(k == e.ENTER && !e.ctrlKey){
50470             ed.completeEdit();
50471             e.stopEvent();
50472             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50473         }else if(k == e.ESC){
50474             ed.cancelEdit();
50475         }
50476         
50477         
50478         if(newCell){
50479             //Roo.log('next cell after edit');
50480             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50481         }
50482     }
50483 });/*
50484  * Based on:
50485  * Ext JS Library 1.1.1
50486  * Copyright(c) 2006-2007, Ext JS, LLC.
50487  *
50488  * Originally Released Under LGPL - original licence link has changed is not relivant.
50489  *
50490  * Fork - LGPL
50491  * <script type="text/javascript">
50492  */
50493  
50494 /**
50495  * @class Roo.grid.EditorGrid
50496  * @extends Roo.grid.Grid
50497  * Class for creating and editable grid.
50498  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50499  * The container MUST have some type of size defined for the grid to fill. The container will be 
50500  * automatically set to position relative if it isn't already.
50501  * @param {Object} dataSource The data model to bind to
50502  * @param {Object} colModel The column model with info about this grid's columns
50503  */
50504 Roo.grid.EditorGrid = function(container, config){
50505     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50506     this.getGridEl().addClass("xedit-grid");
50507
50508     if(!this.selModel){
50509         this.selModel = new Roo.grid.CellSelectionModel();
50510     }
50511
50512     this.activeEditor = null;
50513
50514         this.addEvents({
50515             /**
50516              * @event beforeedit
50517              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50518              * <ul style="padding:5px;padding-left:16px;">
50519              * <li>grid - This grid</li>
50520              * <li>record - The record being edited</li>
50521              * <li>field - The field name being edited</li>
50522              * <li>value - The value for the field being edited.</li>
50523              * <li>row - The grid row index</li>
50524              * <li>column - The grid column index</li>
50525              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50526              * </ul>
50527              * @param {Object} e An edit event (see above for description)
50528              */
50529             "beforeedit" : true,
50530             /**
50531              * @event afteredit
50532              * Fires after a cell is edited. <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 being set</li>
50538              * <li>originalValue - The original value for the field, before the edit.</li>
50539              * <li>row - The grid row index</li>
50540              * <li>column - The grid column index</li>
50541              * </ul>
50542              * @param {Object} e An edit event (see above for description)
50543              */
50544             "afteredit" : true,
50545             /**
50546              * @event validateedit
50547              * Fires after a cell is edited, but before the value is set in the record. 
50548          * You can use this to modify the value being set in the field, Return false
50549              * to cancel the change. The edit event object has the following properties <br />
50550              * <ul style="padding:5px;padding-left:16px;">
50551          * <li>editor - This editor</li>
50552              * <li>grid - This grid</li>
50553              * <li>record - The record being edited</li>
50554              * <li>field - The field name being edited</li>
50555              * <li>value - The value being set</li>
50556              * <li>originalValue - The original value for the field, before the edit.</li>
50557              * <li>row - The grid row index</li>
50558              * <li>column - The grid column index</li>
50559              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50560              * </ul>
50561              * @param {Object} e An edit event (see above for description)
50562              */
50563             "validateedit" : true
50564         });
50565     this.on("bodyscroll", this.stopEditing,  this);
50566     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50567 };
50568
50569 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50570     /**
50571      * @cfg {Number} clicksToEdit
50572      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50573      */
50574     clicksToEdit: 2,
50575
50576     // private
50577     isEditor : true,
50578     // private
50579     trackMouseOver: false, // causes very odd FF errors
50580
50581     onCellDblClick : function(g, row, col){
50582         this.startEditing(row, col);
50583     },
50584
50585     onEditComplete : function(ed, value, startValue){
50586         this.editing = false;
50587         this.activeEditor = null;
50588         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50589         var r = ed.record;
50590         var field = this.colModel.getDataIndex(ed.col);
50591         var e = {
50592             grid: this,
50593             record: r,
50594             field: field,
50595             originalValue: startValue,
50596             value: value,
50597             row: ed.row,
50598             column: ed.col,
50599             cancel:false,
50600             editor: ed
50601         };
50602         if(String(value) !== String(startValue)){
50603             
50604             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50605                 r.set(field, e.value);
50606                 // if we are dealing with a combo box..
50607                 // then we also set the 'name' colum to be the displayField
50608                 if (ed.field.displayField && ed.field.name) {
50609                     r.set(ed.field.name, ed.field.el.dom.value);
50610                 }
50611                 
50612                 delete e.cancel; //?? why!!!
50613                 this.fireEvent("afteredit", e);
50614             }
50615         } else {
50616             this.fireEvent("afteredit", e); // always fire it!
50617         }
50618         this.view.focusCell(ed.row, ed.col);
50619     },
50620
50621     /**
50622      * Starts editing the specified for the specified row/column
50623      * @param {Number} rowIndex
50624      * @param {Number} colIndex
50625      */
50626     startEditing : function(row, col){
50627         this.stopEditing();
50628         if(this.colModel.isCellEditable(col, row)){
50629             this.view.ensureVisible(row, col, true);
50630             var r = this.dataSource.getAt(row);
50631             var field = this.colModel.getDataIndex(col);
50632             var e = {
50633                 grid: this,
50634                 record: r,
50635                 field: field,
50636                 value: r.data[field],
50637                 row: row,
50638                 column: col,
50639                 cancel:false
50640             };
50641             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50642                 this.editing = true;
50643                 var ed = this.colModel.getCellEditor(col, row);
50644                 
50645                 if (!ed) {
50646                     return;
50647                 }
50648                 if(!ed.rendered){
50649                     ed.render(ed.parentEl || document.body);
50650                 }
50651                 ed.field.reset();
50652                 (function(){ // complex but required for focus issues in safari, ie and opera
50653                     ed.row = row;
50654                     ed.col = col;
50655                     ed.record = r;
50656                     ed.on("complete", this.onEditComplete, this, {single: true});
50657                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50658                     this.activeEditor = ed;
50659                     var v = r.data[field];
50660                     ed.startEdit(this.view.getCell(row, col), v);
50661                     // combo's with 'displayField and name set
50662                     if (ed.field.displayField && ed.field.name) {
50663                         ed.field.el.dom.value = r.data[ed.field.name];
50664                     }
50665                     
50666                     
50667                 }).defer(50, this);
50668             }
50669         }
50670     },
50671         
50672     /**
50673      * Stops any active editing
50674      */
50675     stopEditing : function(){
50676         if(this.activeEditor){
50677             this.activeEditor.completeEdit();
50678         }
50679         this.activeEditor = null;
50680     }
50681 });/*
50682  * Based on:
50683  * Ext JS Library 1.1.1
50684  * Copyright(c) 2006-2007, Ext JS, LLC.
50685  *
50686  * Originally Released Under LGPL - original licence link has changed is not relivant.
50687  *
50688  * Fork - LGPL
50689  * <script type="text/javascript">
50690  */
50691
50692 // private - not really -- you end up using it !
50693 // This is a support class used internally by the Grid components
50694
50695 /**
50696  * @class Roo.grid.GridEditor
50697  * @extends Roo.Editor
50698  * Class for creating and editable grid elements.
50699  * @param {Object} config any settings (must include field)
50700  */
50701 Roo.grid.GridEditor = function(field, config){
50702     if (!config && field.field) {
50703         config = field;
50704         field = Roo.factory(config.field, Roo.form);
50705     }
50706     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50707     field.monitorTab = false;
50708 };
50709
50710 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50711     
50712     /**
50713      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50714      */
50715     
50716     alignment: "tl-tl",
50717     autoSize: "width",
50718     hideEl : false,
50719     cls: "x-small-editor x-grid-editor",
50720     shim:false,
50721     shadow:"frame"
50722 });/*
50723  * Based on:
50724  * Ext JS Library 1.1.1
50725  * Copyright(c) 2006-2007, Ext JS, LLC.
50726  *
50727  * Originally Released Under LGPL - original licence link has changed is not relivant.
50728  *
50729  * Fork - LGPL
50730  * <script type="text/javascript">
50731  */
50732   
50733
50734   
50735 Roo.grid.PropertyRecord = Roo.data.Record.create([
50736     {name:'name',type:'string'},  'value'
50737 ]);
50738
50739
50740 Roo.grid.PropertyStore = function(grid, source){
50741     this.grid = grid;
50742     this.store = new Roo.data.Store({
50743         recordType : Roo.grid.PropertyRecord
50744     });
50745     this.store.on('update', this.onUpdate,  this);
50746     if(source){
50747         this.setSource(source);
50748     }
50749     Roo.grid.PropertyStore.superclass.constructor.call(this);
50750 };
50751
50752
50753
50754 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50755     setSource : function(o){
50756         this.source = o;
50757         this.store.removeAll();
50758         var data = [];
50759         for(var k in o){
50760             if(this.isEditableValue(o[k])){
50761                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50762             }
50763         }
50764         this.store.loadRecords({records: data}, {}, true);
50765     },
50766
50767     onUpdate : function(ds, record, type){
50768         if(type == Roo.data.Record.EDIT){
50769             var v = record.data['value'];
50770             var oldValue = record.modified['value'];
50771             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50772                 this.source[record.id] = v;
50773                 record.commit();
50774                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50775             }else{
50776                 record.reject();
50777             }
50778         }
50779     },
50780
50781     getProperty : function(row){
50782        return this.store.getAt(row);
50783     },
50784
50785     isEditableValue: function(val){
50786         if(val && val instanceof Date){
50787             return true;
50788         }else if(typeof val == 'object' || typeof val == 'function'){
50789             return false;
50790         }
50791         return true;
50792     },
50793
50794     setValue : function(prop, value){
50795         this.source[prop] = value;
50796         this.store.getById(prop).set('value', value);
50797     },
50798
50799     getSource : function(){
50800         return this.source;
50801     }
50802 });
50803
50804 Roo.grid.PropertyColumnModel = function(grid, store){
50805     this.grid = grid;
50806     var g = Roo.grid;
50807     g.PropertyColumnModel.superclass.constructor.call(this, [
50808         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50809         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50810     ]);
50811     this.store = store;
50812     this.bselect = Roo.DomHelper.append(document.body, {
50813         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50814             {tag: 'option', value: 'true', html: 'true'},
50815             {tag: 'option', value: 'false', html: 'false'}
50816         ]
50817     });
50818     Roo.id(this.bselect);
50819     var f = Roo.form;
50820     this.editors = {
50821         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50822         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50823         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50824         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50825         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50826     };
50827     this.renderCellDelegate = this.renderCell.createDelegate(this);
50828     this.renderPropDelegate = this.renderProp.createDelegate(this);
50829 };
50830
50831 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50832     
50833     
50834     nameText : 'Name',
50835     valueText : 'Value',
50836     
50837     dateFormat : 'm/j/Y',
50838     
50839     
50840     renderDate : function(dateVal){
50841         return dateVal.dateFormat(this.dateFormat);
50842     },
50843
50844     renderBool : function(bVal){
50845         return bVal ? 'true' : 'false';
50846     },
50847
50848     isCellEditable : function(colIndex, rowIndex){
50849         return colIndex == 1;
50850     },
50851
50852     getRenderer : function(col){
50853         return col == 1 ?
50854             this.renderCellDelegate : this.renderPropDelegate;
50855     },
50856
50857     renderProp : function(v){
50858         return this.getPropertyName(v);
50859     },
50860
50861     renderCell : function(val){
50862         var rv = val;
50863         if(val instanceof Date){
50864             rv = this.renderDate(val);
50865         }else if(typeof val == 'boolean'){
50866             rv = this.renderBool(val);
50867         }
50868         return Roo.util.Format.htmlEncode(rv);
50869     },
50870
50871     getPropertyName : function(name){
50872         var pn = this.grid.propertyNames;
50873         return pn && pn[name] ? pn[name] : name;
50874     },
50875
50876     getCellEditor : function(colIndex, rowIndex){
50877         var p = this.store.getProperty(rowIndex);
50878         var n = p.data['name'], val = p.data['value'];
50879         
50880         if(typeof(this.grid.customEditors[n]) == 'string'){
50881             return this.editors[this.grid.customEditors[n]];
50882         }
50883         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50884             return this.grid.customEditors[n];
50885         }
50886         if(val instanceof Date){
50887             return this.editors['date'];
50888         }else if(typeof val == 'number'){
50889             return this.editors['number'];
50890         }else if(typeof val == 'boolean'){
50891             return this.editors['boolean'];
50892         }else{
50893             return this.editors['string'];
50894         }
50895     }
50896 });
50897
50898 /**
50899  * @class Roo.grid.PropertyGrid
50900  * @extends Roo.grid.EditorGrid
50901  * This class represents the  interface of a component based property grid control.
50902  * <br><br>Usage:<pre><code>
50903  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50904       
50905  });
50906  // set any options
50907  grid.render();
50908  * </code></pre>
50909   
50910  * @constructor
50911  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50912  * The container MUST have some type of size defined for the grid to fill. The container will be
50913  * automatically set to position relative if it isn't already.
50914  * @param {Object} config A config object that sets properties on this grid.
50915  */
50916 Roo.grid.PropertyGrid = function(container, config){
50917     config = config || {};
50918     var store = new Roo.grid.PropertyStore(this);
50919     this.store = store;
50920     var cm = new Roo.grid.PropertyColumnModel(this, store);
50921     store.store.sort('name', 'ASC');
50922     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50923         ds: store.store,
50924         cm: cm,
50925         enableColLock:false,
50926         enableColumnMove:false,
50927         stripeRows:false,
50928         trackMouseOver: false,
50929         clicksToEdit:1
50930     }, config));
50931     this.getGridEl().addClass('x-props-grid');
50932     this.lastEditRow = null;
50933     this.on('columnresize', this.onColumnResize, this);
50934     this.addEvents({
50935          /**
50936              * @event beforepropertychange
50937              * Fires before a property changes (return false to stop?)
50938              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50939              * @param {String} id Record Id
50940              * @param {String} newval New Value
50941          * @param {String} oldval Old Value
50942              */
50943         "beforepropertychange": true,
50944         /**
50945              * @event propertychange
50946              * Fires after a property changes
50947              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50948              * @param {String} id Record Id
50949              * @param {String} newval New Value
50950          * @param {String} oldval Old Value
50951              */
50952         "propertychange": true
50953     });
50954     this.customEditors = this.customEditors || {};
50955 };
50956 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50957     
50958      /**
50959      * @cfg {Object} customEditors map of colnames=> custom editors.
50960      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50961      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50962      * false disables editing of the field.
50963          */
50964     
50965       /**
50966      * @cfg {Object} propertyNames map of property Names to their displayed value
50967          */
50968     
50969     render : function(){
50970         Roo.grid.PropertyGrid.superclass.render.call(this);
50971         this.autoSize.defer(100, this);
50972     },
50973
50974     autoSize : function(){
50975         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50976         if(this.view){
50977             this.view.fitColumns();
50978         }
50979     },
50980
50981     onColumnResize : function(){
50982         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50983         this.autoSize();
50984     },
50985     /**
50986      * Sets the data for the Grid
50987      * accepts a Key => Value object of all the elements avaiable.
50988      * @param {Object} data  to appear in grid.
50989      */
50990     setSource : function(source){
50991         this.store.setSource(source);
50992         //this.autoSize();
50993     },
50994     /**
50995      * Gets all the data from the grid.
50996      * @return {Object} data  data stored in grid
50997      */
50998     getSource : function(){
50999         return this.store.getSource();
51000     }
51001 });/*
51002  * Based on:
51003  * Ext JS Library 1.1.1
51004  * Copyright(c) 2006-2007, Ext JS, LLC.
51005  *
51006  * Originally Released Under LGPL - original licence link has changed is not relivant.
51007  *
51008  * Fork - LGPL
51009  * <script type="text/javascript">
51010  */
51011  
51012 /**
51013  * @class Roo.LoadMask
51014  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51015  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51016  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51017  * element's UpdateManager load indicator and will be destroyed after the initial load.
51018  * @constructor
51019  * Create a new LoadMask
51020  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51021  * @param {Object} config The config object
51022  */
51023 Roo.LoadMask = function(el, config){
51024     this.el = Roo.get(el);
51025     Roo.apply(this, config);
51026     if(this.store){
51027         this.store.on('beforeload', this.onBeforeLoad, this);
51028         this.store.on('load', this.onLoad, this);
51029         this.store.on('loadexception', this.onLoad, this);
51030         this.removeMask = false;
51031     }else{
51032         var um = this.el.getUpdateManager();
51033         um.showLoadIndicator = false; // disable the default indicator
51034         um.on('beforeupdate', this.onBeforeLoad, this);
51035         um.on('update', this.onLoad, this);
51036         um.on('failure', this.onLoad, this);
51037         this.removeMask = true;
51038     }
51039 };
51040
51041 Roo.LoadMask.prototype = {
51042     /**
51043      * @cfg {Boolean} removeMask
51044      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51045      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51046      */
51047     /**
51048      * @cfg {String} msg
51049      * The text to display in a centered loading message box (defaults to 'Loading...')
51050      */
51051     msg : 'Loading...',
51052     /**
51053      * @cfg {String} msgCls
51054      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51055      */
51056     msgCls : 'x-mask-loading',
51057
51058     /**
51059      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51060      * @type Boolean
51061      */
51062     disabled: false,
51063
51064     /**
51065      * Disables the mask to prevent it from being displayed
51066      */
51067     disable : function(){
51068        this.disabled = true;
51069     },
51070
51071     /**
51072      * Enables the mask so that it can be displayed
51073      */
51074     enable : function(){
51075         this.disabled = false;
51076     },
51077
51078     // private
51079     onLoad : function(){
51080         this.el.unmask(this.removeMask);
51081     },
51082
51083     // private
51084     onBeforeLoad : function(){
51085         if(!this.disabled){
51086             this.el.mask(this.msg, this.msgCls);
51087         }
51088     },
51089
51090     // private
51091     destroy : function(){
51092         if(this.store){
51093             this.store.un('beforeload', this.onBeforeLoad, this);
51094             this.store.un('load', this.onLoad, this);
51095             this.store.un('loadexception', this.onLoad, this);
51096         }else{
51097             var um = this.el.getUpdateManager();
51098             um.un('beforeupdate', this.onBeforeLoad, this);
51099             um.un('update', this.onLoad, this);
51100             um.un('failure', this.onLoad, this);
51101         }
51102     }
51103 };/*
51104  * Based on:
51105  * Ext JS Library 1.1.1
51106  * Copyright(c) 2006-2007, Ext JS, LLC.
51107  *
51108  * Originally Released Under LGPL - original licence link has changed is not relivant.
51109  *
51110  * Fork - LGPL
51111  * <script type="text/javascript">
51112  */
51113 Roo.XTemplate = function(){
51114     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51115     var s = this.html;
51116
51117     s = ['<tpl>', s, '</tpl>'].join('');
51118
51119     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51120
51121     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51122     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51123     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51124     var m, id = 0;
51125     var tpls = [];
51126
51127     while(m = s.match(re)){
51128        var m2 = m[0].match(nameRe);
51129        var m3 = m[0].match(ifRe);
51130        var m4 = m[0].match(execRe);
51131        var exp = null, fn = null, exec = null;
51132        var name = m2 && m2[1] ? m2[1] : '';
51133        if(m3){
51134            exp = m3 && m3[1] ? m3[1] : null;
51135            if(exp){
51136                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51137            }
51138        }
51139        if(m4){
51140            exp = m4 && m4[1] ? m4[1] : null;
51141            if(exp){
51142                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51143            }
51144        }
51145        if(name){
51146            switch(name){
51147                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51148                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51149                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51150            }
51151        }
51152        tpls.push({
51153             id: id,
51154             target: name,
51155             exec: exec,
51156             test: fn,
51157             body: m[1]||''
51158         });
51159        s = s.replace(m[0], '{xtpl'+ id + '}');
51160        ++id;
51161     }
51162     for(var i = tpls.length-1; i >= 0; --i){
51163         this.compileTpl(tpls[i]);
51164     }
51165     this.master = tpls[tpls.length-1];
51166     this.tpls = tpls;
51167 };
51168 Roo.extend(Roo.XTemplate, Roo.Template, {
51169
51170     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51171
51172     applySubTemplate : function(id, values, parent){
51173         var t = this.tpls[id];
51174         if(t.test && !t.test.call(this, values, parent)){
51175             return '';
51176         }
51177         if(t.exec && t.exec.call(this, values, parent)){
51178             return '';
51179         }
51180         var vs = t.target ? t.target.call(this, values, parent) : values;
51181         parent = t.target ? values : parent;
51182         if(t.target && vs instanceof Array){
51183             var buf = [];
51184             for(var i = 0, len = vs.length; i < len; i++){
51185                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51186             }
51187             return buf.join('');
51188         }
51189         return t.compiled.call(this, vs, parent);
51190     },
51191
51192     compileTpl : function(tpl){
51193         var fm = Roo.util.Format;
51194         var useF = this.disableFormats !== true;
51195         var sep = Roo.isGecko ? "+" : ",";
51196         var fn = function(m, name, format, args){
51197             if(name.substr(0, 4) == 'xtpl'){
51198                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51199             }
51200             var v;
51201             if(name.indexOf('.') != -1){
51202                 v = name;
51203             }else{
51204                 v = "values['" + name + "']";
51205             }
51206             if(format && useF){
51207                 args = args ? ',' + args : "";
51208                 if(format.substr(0, 5) != "this."){
51209                     format = "fm." + format + '(';
51210                 }else{
51211                     format = 'this.call("'+ format.substr(5) + '", ';
51212                     args = ", values";
51213                 }
51214             }else{
51215                 args= ''; format = "("+v+" === undefined ? '' : ";
51216             }
51217             return "'"+ sep + format + v + args + ")"+sep+"'";
51218         };
51219         var body;
51220         // branched to use + in gecko and [].join() in others
51221         if(Roo.isGecko){
51222             body = "tpl.compiled = function(values, parent){ return '" +
51223                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51224                     "';};";
51225         }else{
51226             body = ["tpl.compiled = function(values, parent){ return ['"];
51227             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51228             body.push("'].join('');};");
51229             body = body.join('');
51230         }
51231         /** eval:var:zzzzzzz */
51232         eval(body);
51233         return this;
51234     },
51235
51236     applyTemplate : function(values){
51237         return this.master.compiled.call(this, values, {});
51238         var s = this.subs;
51239     },
51240
51241     apply : function(){
51242         return this.applyTemplate.apply(this, arguments);
51243     },
51244
51245     compile : function(){return this;}
51246 });
51247
51248 Roo.XTemplate.from = function(el){
51249     el = Roo.getDom(el);
51250     return new Roo.XTemplate(el.value || el.innerHTML);
51251 };/*
51252  * Original code for Roojs - LGPL
51253  * <script type="text/javascript">
51254  */
51255  
51256 /**
51257  * @class Roo.XComponent
51258  * A delayed Element creator...
51259  * Or a way to group chunks of interface together.
51260  * 
51261  * Mypart.xyx = new Roo.XComponent({
51262
51263     parent : 'Mypart.xyz', // empty == document.element.!!
51264     order : '001',
51265     name : 'xxxx'
51266     region : 'xxxx'
51267     disabled : function() {} 
51268      
51269     tree : function() { // return an tree of xtype declared components
51270         var MODULE = this;
51271         return 
51272         {
51273             xtype : 'NestedLayoutPanel',
51274             // technicall
51275         }
51276      ]
51277  *})
51278  *
51279  *
51280  * It can be used to build a big heiracy, with parent etc.
51281  * or you can just use this to render a single compoent to a dom element
51282  * MYPART.render(Roo.Element | String(id) | dom_element )
51283  * 
51284  * @extends Roo.util.Observable
51285  * @constructor
51286  * @param cfg {Object} configuration of component
51287  * 
51288  */
51289 Roo.XComponent = function(cfg) {
51290     Roo.apply(this, cfg);
51291     this.addEvents({ 
51292         /**
51293              * @event built
51294              * Fires when this the componnt is built
51295              * @param {Roo.XComponent} c the component
51296              */
51297         'built' : true,
51298         /**
51299              * @event buildcomplete
51300              * Fires on the top level element when all elements have been built
51301              * @param {Roo.XComponent} c the top level component.
51302          */
51303         'buildcomplete' : true
51304         
51305     });
51306     
51307     Roo.XComponent.register(this);
51308     this.modules = false;
51309     this.el = false; // where the layout goes..
51310     
51311     
51312 }
51313 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51314     /**
51315      * @property el
51316      * The created element (with Roo.factory())
51317      * @type {Roo.Layout}
51318      */
51319     el  : false,
51320     
51321     /**
51322      * @property el
51323      * for BC  - use el in new code
51324      * @type {Roo.Layout}
51325      */
51326     panel : false,
51327     
51328     /**
51329      * @property layout
51330      * for BC  - use el in new code
51331      * @type {Roo.Layout}
51332      */
51333     layout : false,
51334     
51335      /**
51336      * @cfg {Function|boolean} disabled
51337      * If this module is disabled by some rule, return true from the funtion
51338      */
51339     disabled : false,
51340     
51341     /**
51342      * @cfg {String} parent 
51343      * Name of parent element which it get xtype added to..
51344      */
51345     parent: false,
51346     
51347     /**
51348      * @cfg {String} order
51349      * Used to set the order in which elements are created (usefull for multiple tabs)
51350      */
51351     
51352     order : false,
51353     /**
51354      * @cfg {String} name
51355      * String to display while loading.
51356      */
51357     name : false,
51358     /**
51359      * @cfg {String} region
51360      * Region to render component to (defaults to center)
51361      */
51362     region : 'center',
51363     
51364     /**
51365      * @cfg {Array} items
51366      * A single item array - the first element is the root of the tree..
51367      * It's done this way to stay compatible with the Xtype system...
51368      */
51369     items : false,
51370     
51371     
51372      /**
51373      * render
51374      * render element to dom or tree
51375      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51376      */
51377     
51378     render : function(el)
51379     {
51380         
51381         el = el || false;
51382         
51383         if (!el && typeof(m.parent) == 'string' && m.parent[0] == '#') {
51384             // if parent is a '#.....' string, then let's use that..
51385             this.parent = false;
51386             el = Roo.get(m.substr(1));
51387         }
51388         if (!this.parent) {
51389             
51390             el = el ? Roo.get(el) : false;
51391             
51392             // it's a top level one..
51393             this.parent =  {
51394                 el : new Roo.BorderLayout(el || document.body, {
51395                 
51396                      center: {
51397                          titlebar: false,
51398                          autoScroll:false,
51399                          closeOnTab: true,
51400                          tabPosition: 'top',
51401                           //resizeTabs: true,
51402                          alwaysShowTabs: el ? false :  true,
51403                          hideTabs: el ? true :  false,
51404                          minTabWidth: 140
51405                      }
51406                  })
51407             }
51408         }
51409             
51410         var tree = this.tree();
51411         tree.region = tree.region || this.region;
51412         this.el = this.parent.el.addxtype(tree);
51413         this.fireEvent('built', this);
51414         
51415         this.panel = this.el;
51416         this.layout = this.panel.layout;    
51417          
51418     }
51419     
51420 });
51421
51422 Roo.apply(Roo.XComponent, {
51423     
51424     /**
51425      * @property  buildCompleted
51426      * True when the builder has completed building the interface.
51427      * @type Boolean
51428      */
51429     buildCompleted : false,
51430      
51431     /**
51432      * @property  topModule
51433      * the upper most module - uses document.element as it's constructor.
51434      * @type Object
51435      */
51436      
51437     topModule  : false,
51438       
51439     /**
51440      * @property  modules
51441      * array of modules to be created by registration system.
51442      * @type {Array} of Roo.XComponent
51443      */
51444     
51445     modules : [],
51446     /**
51447      * @property  elmodules
51448      * array of modules to be created by which use #ID 
51449      * @type {Array} of Roo.XComponent
51450      */
51451      
51452     elmodules : [],
51453
51454     
51455     /**
51456      * Register components to be built later.
51457      *
51458      * This solves the following issues
51459      * - Building is not done on page load, but after an authentication process has occured.
51460      * - Interface elements are registered on page load
51461      * - Parent Interface elements may not be loaded before child, so this handles that..
51462      * 
51463      *
51464      * example:
51465      * 
51466      * MyApp.register({
51467           order : '000001',
51468           module : 'Pman.Tab.projectMgr',
51469           region : 'center',
51470           parent : 'Pman.layout',
51471           disabled : false,  // or use a function..
51472         })
51473      
51474      * * @param {Object} details about module
51475      */
51476     register : function(obj) {
51477         this.modules.push(obj);
51478          
51479     },
51480     /**
51481      * convert a string to an object..
51482      * eg. 'AAA.BBB' -> finds AAA.BBB
51483
51484      */
51485     
51486     toObject : function(str)
51487     {
51488         if (!str || typeof(str) == 'object') {
51489             return str;
51490         }
51491         if (str[0]=='#') {
51492             return str;
51493         }
51494
51495         var ar = str.split('.');
51496         var rt, o;
51497         rt = ar.shift();
51498             /** eval:var:o */
51499         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51500         if (o === false) {
51501             throw "Module not found : " + str;
51502         }
51503         Roo.each(ar, function(e) {
51504             if (typeof(o[e]) == 'undefined') {
51505                 throw "Module not found : " + str;
51506             }
51507             o = o[e];
51508         });
51509         return o;
51510         
51511     },
51512     
51513     
51514     /**
51515      * move modules into their correct place in the tree..
51516      * 
51517      */
51518     preBuild : function ()
51519     {
51520         
51521         Roo.each(this.modules , function (obj)
51522         {
51523             obj.parent = this.toObject(obj.parent);
51524             
51525             if (!obj.parent) {
51526                 this.topModule = obj;
51527                 return;
51528             }
51529             if (typeof(obj.parent) == 'string') {
51530                 this.elmodules.push(obj);
51531                 return;
51532             }
51533             
51534             if (!obj.parent.modules) {
51535                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51536                     function(o) { return o.order + '' }
51537                 );
51538             }
51539             
51540             obj.parent.modules.add(obj);
51541         }, this);
51542     },
51543     
51544      /**
51545      * make a list of modules to build.
51546      * @return {Array} list of modules. 
51547      */ 
51548     
51549     buildOrder : function()
51550     {
51551         var _this = this;
51552         var cmp = function(a,b) {   
51553             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51554         };
51555         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51556             throw "No top level modules to build";
51557         }
51558         
51559         // make a flat list in order of modules to build.
51560         var mods = this.topModule ? [ this.topModule ] : [];
51561         Roo.each(this.elmodules,function(e) { mods.push(e) });
51562
51563         
51564         // add modules to their parents..
51565         var addMod = function(m) {
51566            // Roo.debug && Roo.log(m.modKey);
51567             
51568             mods.push(m);
51569             if (m.modules) {
51570                 m.modules.keySort('ASC',  cmp );
51571                 m.modules.each(addMod);
51572             }
51573             // not sure if this is used any more..
51574             if (m.finalize) {
51575                 m.finalize.name = m.name + " (clean up) ";
51576                 mods.push(m.finalize);
51577             }
51578             
51579         }
51580         if (this.topModule) { 
51581             this.topModule.modules.keySort('ASC',  cmp );
51582             this.topModule.modules.each(addMod);
51583         }
51584         return mods;
51585     },
51586     
51587      /**
51588      * Build the registered modules.
51589      * @param {Object} parent element.
51590      * @param {Function} optional method to call after module has been added.
51591      * 
51592      */ 
51593    
51594     build : function() 
51595     {
51596         
51597         this.preBuild();
51598         var mods = this.buildOrder();
51599       
51600         //this.allmods = mods;
51601         //Roo.debug && Roo.log(mods);
51602         //return;
51603         if (!mods.length) { // should not happen
51604             throw "NO modules!!!";
51605         }
51606         
51607         
51608         
51609         // flash it up as modal - so we store the mask!?
51610         Roo.MessageBox.show({ title: 'loading' });
51611         Roo.MessageBox.show({
51612            title: "Please wait...",
51613            msg: "Building Interface...",
51614            width:450,
51615            progress:true,
51616            closable:false,
51617            modal: false
51618           
51619         });
51620         var total = mods.length;
51621         
51622         var _this = this;
51623         var progressRun = function() {
51624             if (!mods.length) {
51625                 Roo.debug && Roo.log('hide?');
51626                 Roo.MessageBox.hide();
51627                 if (_this.topModule) { 
51628                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51629                 }
51630                 // THE END...
51631                 return false;   
51632             }
51633             
51634             var m = mods.shift();
51635             
51636             
51637             Roo.debug && Roo.log(m);
51638             // not sure if this is supported any more.. - modules that are are just function
51639             if (typeof(m) == 'function') { 
51640                 m.call(this);
51641                 return progressRun.defer(10, _this);
51642             } 
51643             
51644             
51645             
51646             Roo.MessageBox.updateProgress(
51647                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51648                     " of " + total + 
51649                     (m.name ? (' - ' + m.name) : '')
51650                     );
51651             
51652          
51653             // is the module disabled?
51654             var disabled = (typeof(m.disabled) == 'function') ?
51655                 m.disabled.call(m.module.disabled) : m.disabled;    
51656             
51657             
51658             if (disabled) {
51659                 return progressRun(); // we do not update the display!
51660             }
51661             
51662             // now build 
51663             
51664             m.render();
51665             // it's 10 on top level, and 1 on others??? why...
51666             return progressRun.defer(10, _this);
51667              
51668         }
51669         progressRun.defer(1, _this);
51670      
51671         
51672         
51673     }
51674     
51675      
51676    
51677     
51678     
51679 });
51680  //<script type="text/javascript">
51681
51682
51683 /**
51684  * @class Roo.Login
51685  * @extends Roo.LayoutDialog
51686  * A generic Login Dialog..... - only one needed in theory!?!?
51687  *
51688  * Fires XComponent builder on success...
51689  * 
51690  * Sends 
51691  *    username,password, lang = for login actions.
51692  *    check = 1 for periodic checking that sesion is valid.
51693  *    passwordRequest = email request password
51694  *    logout = 1 = to logout
51695  * 
51696  * Affects: (this id="????" elements)
51697  *   loading  (removed) (used to indicate application is loading)
51698  *   loading-mask (hides) (used to hide application when it's building loading)
51699  *   
51700  * 
51701  * Usage: 
51702  *    
51703  * 
51704  * Myapp.login = Roo.Login({
51705      url: xxxx,
51706    
51707      realm : 'Myapp', 
51708      
51709      
51710      method : 'POST',
51711      
51712      
51713      * 
51714  })
51715  * 
51716  * 
51717  * 
51718  **/
51719  
51720 Roo.Login = function(cfg)
51721 {
51722     this.addEvents({
51723         'refreshed' : true
51724     });
51725     
51726     Roo.apply(this,cfg);
51727     
51728     Roo.onReady(function() {
51729         this.onLoad();
51730     }, this);
51731     // call parent..
51732     
51733    
51734     Roo.Login.superclass.constructor.call(this, this);
51735     //this.addxtype(this.items[0]);
51736     
51737     
51738 }
51739
51740
51741 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51742     
51743     /**
51744      * @cfg {String} method
51745      * Method used to query for login details.
51746      */
51747     
51748     method : 'POST',
51749     /**
51750      * @cfg {String} url
51751      * URL to query login data. - eg. baseURL + '/Login.php'
51752      */
51753     url : '',
51754     
51755     /**
51756      * @property user
51757      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51758      * @type {Object} 
51759      */
51760     user : false,
51761     /**
51762      * @property checkFails
51763      * Number of times we have attempted to get authentication check, and failed.
51764      * @type {Number} 
51765      */
51766     checkFails : 0,
51767       /**
51768      * @property intervalID
51769      * The window interval that does the constant login checking.
51770      * @type {Number} 
51771      */
51772     intervalID : 0,
51773     
51774     
51775     onLoad : function() // called on page load...
51776     {
51777         // load 
51778          
51779         if (Roo.get('loading')) { // clear any loading indicator..
51780             Roo.get('loading').remove();
51781         }
51782         
51783         //this.switchLang('en'); // set the language to english..
51784        
51785         this.check({
51786             success:  function(response, opts)  {  // check successfull...
51787             
51788                 var res = this.processResponse(response);
51789                 this.checkFails =0;
51790                 if (!res.success) { // error!
51791                     this.checkFails = 5;
51792                     //console.log('call failure');
51793                     return this.failure(response,opts);
51794                 }
51795                 
51796                 if (!res.data.id) { // id=0 == login failure.
51797                     return this.show();
51798                 }
51799                 
51800                               
51801                         //console.log(success);
51802                 this.fillAuth(res.data);   
51803                 this.checkFails =0;
51804                 Roo.XComponent.build();
51805             },
51806             failure : this.show
51807         });
51808         
51809     }, 
51810     
51811     
51812     check: function(cfg) // called every so often to refresh cookie etc..
51813     {
51814         if (cfg.again) { // could be undefined..
51815             this.checkFails++;
51816         } else {
51817             this.checkFails = 0;
51818         }
51819         var _this = this;
51820         if (this.sending) {
51821             if ( this.checkFails > 4) {
51822                 Roo.MessageBox.alert("Error",  
51823                     "Error getting authentication status. - try reloading, or wait a while", function() {
51824                         _this.sending = false;
51825                     }); 
51826                 return;
51827             }
51828             cfg.again = true;
51829             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51830             return;
51831         }
51832         this.sending = true;
51833         
51834         Roo.Ajax.request({  
51835             url: this.url,
51836             params: {
51837                 getAuthUser: true
51838             },  
51839             method: this.method,
51840             success:  cfg.success || this.success,
51841             failure : cfg.failure || this.failure,
51842             scope : this,
51843             callCfg : cfg
51844               
51845         });  
51846     }, 
51847     
51848     
51849     logout: function()
51850     {
51851         window.onbeforeunload = function() { }; // false does not work for IE..
51852         this.user = false;
51853         var _this = this;
51854         
51855         Roo.Ajax.request({  
51856             url: this.url,
51857             params: {
51858                 logout: 1
51859             },  
51860             method: 'GET',
51861             failure : function() {
51862                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51863                     document.location = document.location.toString() + '?ts=' + Math.random();
51864                 });
51865                 
51866             },
51867             success : function() {
51868                 _this.user = false;
51869                 this.checkFails =0;
51870                 // fixme..
51871                 document.location = document.location.toString() + '?ts=' + Math.random();
51872             }
51873               
51874               
51875         }); 
51876     },
51877     
51878     processResponse : function (response)
51879     {
51880         var res = '';
51881         try {
51882             res = Roo.decode(response.responseText);
51883             // oops...
51884             if (typeof(res) != 'object') {
51885                 res = { success : false, errorMsg : res, errors : true };
51886             }
51887             if (typeof(res.success) == 'undefined') {
51888                 res.success = false;
51889             }
51890             
51891         } catch(e) {
51892             res = { success : false,  errorMsg : response.responseText, errors : true };
51893         }
51894         return res;
51895     },
51896     
51897     success : function(response, opts)  // check successfull...
51898     {  
51899         this.sending = false;
51900         var res = this.processResponse(response);
51901         if (!res.success) {
51902             return this.failure(response, opts);
51903         }
51904         if (!res.data || !res.data.id) {
51905             return this.failure(response,opts);
51906         }
51907         //console.log(res);
51908         this.fillAuth(res.data);
51909         
51910         this.checkFails =0;
51911         
51912     },
51913     
51914     
51915     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51916     {
51917         this.authUser = -1;
51918         this.sending = false;
51919         var res = this.processResponse(response);
51920         //console.log(res);
51921         if ( this.checkFails > 2) {
51922         
51923             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51924                 "Error getting authentication status. - try reloading"); 
51925             return;
51926         }
51927         opts.callCfg.again = true;
51928         this.check.defer(1000, this, [ opts.callCfg ]);
51929         return;  
51930     },
51931     
51932     
51933     
51934     fillAuth: function(au) {
51935         this.startAuthCheck();
51936         this.authUserId = au.id;
51937         this.authUser = au;
51938         this.lastChecked = new Date();
51939         this.fireEvent('refreshed', au);
51940         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51941         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51942         au.lang = au.lang || 'en';
51943         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51944         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51945         this.switchLang(au.lang );
51946         
51947      
51948         // open system... - -on setyp..
51949         if (this.authUserId  < 0) {
51950             Roo.MessageBox.alert("Warning", 
51951                 "This is an open system - please set up a admin user with a password.");  
51952         }
51953          
51954         //Pman.onload(); // which should do nothing if it's a re-auth result...
51955         
51956              
51957     },
51958     
51959     startAuthCheck : function() // starter for timeout checking..
51960     {
51961         if (this.intervalID) { // timer already in place...
51962             return false;
51963         }
51964         var _this = this;
51965         this.intervalID =  window.setInterval(function() {
51966               _this.check(false);
51967             }, 120000); // every 120 secs = 2mins..
51968         
51969         
51970     },
51971          
51972     
51973     switchLang : function (lang) 
51974     {
51975         _T = typeof(_T) == 'undefined' ? false : _T;
51976           if (!_T || !lang.length) {
51977             return;
51978         }
51979         
51980         if (!_T && lang != 'en') {
51981             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51982             return;
51983         }
51984         
51985         if (typeof(_T.en) == 'undefined') {
51986             _T.en = {};
51987             Roo.apply(_T.en, _T);
51988         }
51989         
51990         if (typeof(_T[lang]) == 'undefined') {
51991             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51992             return;
51993         }
51994         
51995         
51996         Roo.apply(_T, _T[lang]);
51997         // just need to set the text values for everything...
51998         var _this = this;
51999         /* this will not work ...
52000         if (this.form) { 
52001             
52002                
52003             function formLabel(name, val) {
52004                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52005             }
52006             
52007             formLabel('password', "Password"+':');
52008             formLabel('username', "Email Address"+':');
52009             formLabel('lang', "Language"+':');
52010             this.dialog.setTitle("Login");
52011             this.dialog.buttons[0].setText("Forgot Password");
52012             this.dialog.buttons[1].setText("Login");
52013         }
52014         */
52015         
52016         
52017     },
52018     
52019     
52020     title: "Login",
52021     modal: true,
52022     width:  350,
52023     //height: 230,
52024     height: 180,
52025     shadow: true,
52026     minWidth:200,
52027     minHeight:180,
52028     //proxyDrag: true,
52029     closable: false,
52030     draggable: false,
52031     collapsible: false,
52032     resizable: false,
52033     center: {  // needed??
52034         autoScroll:false,
52035         titlebar: false,
52036        // tabPosition: 'top',
52037         hideTabs: true,
52038         closeOnTab: true,
52039         alwaysShowTabs: false
52040     } ,
52041     listeners : {
52042         
52043         show  : function(dlg)
52044         {
52045             //console.log(this);
52046             this.form = this.layout.getRegion('center').activePanel.form;
52047             this.form.dialog = dlg;
52048             this.buttons[0].form = this.form;
52049             this.buttons[0].dialog = dlg;
52050             this.buttons[1].form = this.form;
52051             this.buttons[1].dialog = dlg;
52052            
52053            //this.resizeToLogo.defer(1000,this);
52054             // this is all related to resizing for logos..
52055             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52056            //// if (!sz) {
52057              //   this.resizeToLogo.defer(1000,this);
52058              //   return;
52059            // }
52060             //var w = Ext.lib.Dom.getViewWidth() - 100;
52061             //var h = Ext.lib.Dom.getViewHeight() - 100;
52062             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52063             //this.center();
52064             if (this.disabled) {
52065                 this.hide();
52066                 return;
52067             }
52068             
52069             if (this.user.id < 0) { // used for inital setup situations.
52070                 return;
52071             }
52072             
52073             if (this.intervalID) {
52074                 // remove the timer
52075                 window.clearInterval(this.intervalID);
52076                 this.intervalID = false;
52077             }
52078             
52079             
52080             if (Roo.get('loading')) {
52081                 Roo.get('loading').remove();
52082             }
52083             if (Roo.get('loading-mask')) {
52084                 Roo.get('loading-mask').hide();
52085             }
52086             
52087             //incomming._node = tnode;
52088             this.form.reset();
52089             //this.dialog.modal = !modal;
52090             //this.dialog.show();
52091             this.el.unmask(); 
52092             
52093             
52094             this.form.setValues({
52095                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52096                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52097             });
52098             
52099             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52100             if (this.form.findField('username').getValue().length > 0 ){
52101                 this.form.findField('password').focus();
52102             } else {
52103                this.form.findField('username').focus();
52104             }
52105     
52106         }
52107     },
52108     items : [
52109          {
52110        
52111             xtype : 'ContentPanel',
52112             xns : Roo,
52113             region: 'center',
52114             fitToFrame : true,
52115             
52116             items : [
52117     
52118                 {
52119                
52120                     xtype : 'Form',
52121                     xns : Roo.form,
52122                     labelWidth: 100,
52123                     style : 'margin: 10px;',
52124                     
52125                     listeners : {
52126                         actionfailed : function(f, act) {
52127                             // form can return { errors: .... }
52128                                 
52129                             //act.result.errors // invalid form element list...
52130                             //act.result.errorMsg// invalid form element list...
52131                             
52132                             this.dialog.el.unmask();
52133                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52134                                         "Login failed - communication error - try again.");
52135                                       
52136                         },
52137                         actioncomplete: function(re, act) {
52138                              
52139                             Roo.state.Manager.set(
52140                                 this.dialog.realm + '.username',  
52141                                     this.findField('username').getValue()
52142                             );
52143                             Roo.state.Manager.set(
52144                                 this.dialog.realm + '.lang',  
52145                                 this.findField('lang').getValue() 
52146                             );
52147                             
52148                             this.dialog.fillAuth(act.result.data);
52149                               
52150                             this.dialog.hide();
52151                             
52152                             if (Roo.get('loading-mask')) {
52153                                 Roo.get('loading-mask').show();
52154                             }
52155                             Roo.XComponent.build();
52156                             
52157                              
52158                             
52159                         }
52160                     },
52161                     items : [
52162                         {
52163                             xtype : 'TextField',
52164                             xns : Roo.form,
52165                             fieldLabel: "Email Address",
52166                             name: 'username',
52167                             width:200,
52168                             autoCreate : {tag: "input", type: "text", size: "20"}
52169                         },
52170                         {
52171                             xtype : 'TextField',
52172                             xns : Roo.form,
52173                             fieldLabel: "Password",
52174                             inputType: 'password',
52175                             name: 'password',
52176                             width:200,
52177                             autoCreate : {tag: "input", type: "text", size: "20"},
52178                             listeners : {
52179                                 specialkey : function(e,ev) {
52180                                     if (ev.keyCode == 13) {
52181                                         this.form.dialog.el.mask("Logging in");
52182                                         this.form.doAction('submit', {
52183                                             url: this.form.dialog.url,
52184                                             method: this.form.dialog.method
52185                                         });
52186                                     }
52187                                 }
52188                             }  
52189                         },
52190                         {
52191                             xtype : 'ComboBox',
52192                             xns : Roo.form,
52193                             fieldLabel: "Language",
52194                             name : 'langdisp',
52195                             store: {
52196                                 xtype : 'SimpleStore',
52197                                 fields: ['lang', 'ldisp'],
52198                                 data : [
52199                                     [ 'en', 'English' ],
52200                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52201                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52202                                 ]
52203                             },
52204                             
52205                             valueField : 'lang',
52206                             hiddenName:  'lang',
52207                             width: 200,
52208                             displayField:'ldisp',
52209                             typeAhead: false,
52210                             editable: false,
52211                             mode: 'local',
52212                             triggerAction: 'all',
52213                             emptyText:'Select a Language...',
52214                             selectOnFocus:true,
52215                             listeners : {
52216                                 select :  function(cb, rec, ix) {
52217                                     this.form.switchLang(rec.data.lang);
52218                                 }
52219                             }
52220                         
52221                         }
52222                     ]
52223                 }
52224                   
52225                 
52226             ]
52227         }
52228     ],
52229     buttons : [
52230         {
52231             xtype : 'Button',
52232             xns : 'Roo',
52233             text : "Forgot Password",
52234             listeners : {
52235                 click : function() {
52236                     //console.log(this);
52237                     var n = this.form.findField('username').getValue();
52238                     if (!n.length) {
52239                         Roo.MessageBox.alert("Error", "Fill in your email address");
52240                         return;
52241                     }
52242                     Roo.Ajax.request({
52243                         url: this.dialog.url,
52244                         params: {
52245                             passwordRequest: n
52246                         },
52247                         method: this.dialog.method,
52248                         success:  function(response, opts)  {  // check successfull...
52249                         
52250                             var res = this.dialog.processResponse(response);
52251                             if (!res.success) { // error!
52252                                Roo.MessageBox.alert("Error" ,
52253                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52254                                return;
52255                             }
52256                             Roo.MessageBox.alert("Notice" ,
52257                                 "Please check you email for the Password Reset message");
52258                         },
52259                         failure : function() {
52260                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52261                         }
52262                         
52263                     });
52264                 }
52265             }
52266         },
52267         {
52268             xtype : 'Button',
52269             xns : 'Roo',
52270             text : "Login",
52271             listeners : {
52272                 
52273                 click : function () {
52274                         
52275                     this.dialog.el.mask("Logging in");
52276                     this.form.doAction('submit', {
52277                             url: this.dialog.url,
52278                             method: this.dialog.method
52279                     });
52280                 }
52281             }
52282         }
52283     ]
52284   
52285   
52286 })
52287  
52288
52289
52290