Roo/form/ComboBoxArray.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 this.timeout[o.tId] = window.setTimeout(function() {
2714                     oConn.abort(o, callback, true);
2715                 }, callback.timeout);
2716             }
2717
2718             this.poll[o.tId] = window.setInterval(
2719                     function() {
2720                         if (o.conn && o.conn.readyState == 4) {
2721                             window.clearInterval(oConn.poll[o.tId]);
2722                             delete oConn.poll[o.tId];
2723
2724                             if(callback && callback.timeout) {
2725                                 window.clearTimeout(oConn.timeout[o.tId]);
2726                                 delete oConn.timeout[o.tId];
2727                             }
2728
2729                             oConn.handleTransactionResponse(o, callback);
2730                         }
2731                     }
2732                     , this.pollInterval);
2733         },
2734
2735         handleTransactionResponse:function(o, callback, isAbort)
2736         {
2737
2738             if (!callback) {
2739                 this.releaseObject(o);
2740                 return;
2741             }
2742
2743             var httpStatus, responseObject;
2744
2745             try
2746             {
2747                 if (o.conn.status !== undefined && o.conn.status != 0) {
2748                     httpStatus = o.conn.status;
2749                 }
2750                 else {
2751                     httpStatus = 13030;
2752                 }
2753             }
2754             catch(e) {
2755
2756
2757                 httpStatus = 13030;
2758             }
2759
2760             if (httpStatus >= 200 && httpStatus < 300) {
2761                 responseObject = this.createResponseObject(o, callback.argument);
2762                 if (callback.success) {
2763                     if (!callback.scope) {
2764                         callback.success(responseObject);
2765                     }
2766                     else {
2767
2768
2769                         callback.success.apply(callback.scope, [responseObject]);
2770                     }
2771                 }
2772             }
2773             else {
2774                 switch (httpStatus) {
2775
2776                     case 12002:
2777                     case 12029:
2778                     case 12030:
2779                     case 12031:
2780                     case 12152:
2781                     case 13030:
2782                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2783                         if (callback.failure) {
2784                             if (!callback.scope) {
2785                                 callback.failure(responseObject);
2786                             }
2787                             else {
2788                                 callback.failure.apply(callback.scope, [responseObject]);
2789                             }
2790                         }
2791                         break;
2792                     default:
2793                         responseObject = this.createResponseObject(o, callback.argument);
2794                         if (callback.failure) {
2795                             if (!callback.scope) {
2796                                 callback.failure(responseObject);
2797                             }
2798                             else {
2799                                 callback.failure.apply(callback.scope, [responseObject]);
2800                             }
2801                         }
2802                 }
2803             }
2804
2805             this.releaseObject(o);
2806             responseObject = null;
2807         },
2808
2809         createResponseObject:function(o, callbackArg)
2810         {
2811             var obj = {};
2812             var headerObj = {};
2813
2814             try
2815             {
2816                 var headerStr = o.conn.getAllResponseHeaders();
2817                 var header = headerStr.split('\n');
2818                 for (var i = 0; i < header.length; i++) {
2819                     var delimitPos = header[i].indexOf(':');
2820                     if (delimitPos != -1) {
2821                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2822                     }
2823                 }
2824             }
2825             catch(e) {
2826             }
2827
2828             obj.tId = o.tId;
2829             obj.status = o.conn.status;
2830             obj.statusText = o.conn.statusText;
2831             obj.getResponseHeader = headerObj;
2832             obj.getAllResponseHeaders = headerStr;
2833             obj.responseText = o.conn.responseText;
2834             obj.responseXML = o.conn.responseXML;
2835
2836             if (typeof callbackArg !== undefined) {
2837                 obj.argument = callbackArg;
2838             }
2839
2840             return obj;
2841         },
2842
2843         createExceptionObject:function(tId, callbackArg, isAbort)
2844         {
2845             var COMM_CODE = 0;
2846             var COMM_ERROR = 'communication failure';
2847             var ABORT_CODE = -1;
2848             var ABORT_ERROR = 'transaction aborted';
2849
2850             var obj = {};
2851
2852             obj.tId = tId;
2853             if (isAbort) {
2854                 obj.status = ABORT_CODE;
2855                 obj.statusText = ABORT_ERROR;
2856             }
2857             else {
2858                 obj.status = COMM_CODE;
2859                 obj.statusText = COMM_ERROR;
2860             }
2861
2862             if (callbackArg) {
2863                 obj.argument = callbackArg;
2864             }
2865
2866             return obj;
2867         },
2868
2869         initHeader:function(label, value, isDefault)
2870         {
2871             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2872
2873             if (headerObj[label] === undefined) {
2874                 headerObj[label] = value;
2875             }
2876             else {
2877
2878
2879                 headerObj[label] = value + "," + headerObj[label];
2880             }
2881
2882             if (isDefault) {
2883                 this.hasDefaultHeaders = true;
2884             }
2885             else {
2886                 this.hasHeaders = true;
2887             }
2888         },
2889
2890
2891         setHeader:function(o)
2892         {
2893             if (this.hasDefaultHeaders) {
2894                 for (var prop in this.defaultHeaders) {
2895                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2896                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2897                     }
2898                 }
2899             }
2900
2901             if (this.hasHeaders) {
2902                 for (var prop in this.headers) {
2903                     if (this.headers.hasOwnProperty(prop)) {
2904                         o.conn.setRequestHeader(prop, this.headers[prop]);
2905                     }
2906                 }
2907                 this.headers = {};
2908                 this.hasHeaders = false;
2909             }
2910         },
2911
2912         resetDefaultHeaders:function() {
2913             delete this.defaultHeaders;
2914             this.defaultHeaders = {};
2915             this.hasDefaultHeaders = false;
2916         },
2917
2918         abort:function(o, callback, isTimeout)
2919         {
2920             if(this.isCallInProgress(o)) {
2921                 o.conn.abort();
2922                 window.clearInterval(this.poll[o.tId]);
2923                 delete this.poll[o.tId];
2924                 if (isTimeout) {
2925                     delete this.timeout[o.tId];
2926                 }
2927
2928                 this.handleTransactionResponse(o, callback, true);
2929
2930                 return true;
2931             }
2932             else {
2933                 return false;
2934             }
2935         },
2936
2937
2938         isCallInProgress:function(o)
2939         {
2940             if (o && o.conn) {
2941                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2942             }
2943             else {
2944
2945                 return false;
2946             }
2947         },
2948
2949
2950         releaseObject:function(o)
2951         {
2952
2953             o.conn = null;
2954
2955             o = null;
2956         },
2957
2958         activeX:[
2959         'MSXML2.XMLHTTP.3.0',
2960         'MSXML2.XMLHTTP',
2961         'Microsoft.XMLHTTP'
2962         ]
2963
2964
2965     };
2966 })();/*
2967  * Portions of this file are based on pieces of Yahoo User Interface Library
2968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2969  * YUI licensed under the BSD License:
2970  * http://developer.yahoo.net/yui/license.txt
2971  * <script type="text/javascript">
2972  *
2973  */
2974
2975 Roo.lib.Region = function(t, r, b, l) {
2976     this.top = t;
2977     this[1] = t;
2978     this.right = r;
2979     this.bottom = b;
2980     this.left = l;
2981     this[0] = l;
2982 };
2983
2984
2985 Roo.lib.Region.prototype = {
2986     contains : function(region) {
2987         return ( region.left >= this.left &&
2988                  region.right <= this.right &&
2989                  region.top >= this.top &&
2990                  region.bottom <= this.bottom    );
2991
2992     },
2993
2994     getArea : function() {
2995         return ( (this.bottom - this.top) * (this.right - this.left) );
2996     },
2997
2998     intersect : function(region) {
2999         var t = Math.max(this.top, region.top);
3000         var r = Math.min(this.right, region.right);
3001         var b = Math.min(this.bottom, region.bottom);
3002         var l = Math.max(this.left, region.left);
3003
3004         if (b >= t && r >= l) {
3005             return new Roo.lib.Region(t, r, b, l);
3006         } else {
3007             return null;
3008         }
3009     },
3010     union : function(region) {
3011         var t = Math.min(this.top, region.top);
3012         var r = Math.max(this.right, region.right);
3013         var b = Math.max(this.bottom, region.bottom);
3014         var l = Math.min(this.left, region.left);
3015
3016         return new Roo.lib.Region(t, r, b, l);
3017     },
3018
3019     adjust : function(t, l, b, r) {
3020         this.top += t;
3021         this.left += l;
3022         this.right += r;
3023         this.bottom += b;
3024         return this;
3025     }
3026 };
3027
3028 Roo.lib.Region.getRegion = function(el) {
3029     var p = Roo.lib.Dom.getXY(el);
3030
3031     var t = p[1];
3032     var r = p[0] + el.offsetWidth;
3033     var b = p[1] + el.offsetHeight;
3034     var l = p[0];
3035
3036     return new Roo.lib.Region(t, r, b, l);
3037 };
3038 /*
3039  * Portions of this file are based on pieces of Yahoo User Interface Library
3040  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3041  * YUI licensed under the BSD License:
3042  * http://developer.yahoo.net/yui/license.txt
3043  * <script type="text/javascript">
3044  *
3045  */
3046 //@@dep Roo.lib.Region
3047
3048
3049 Roo.lib.Point = function(x, y) {
3050     if (x instanceof Array) {
3051         y = x[1];
3052         x = x[0];
3053     }
3054     this.x = this.right = this.left = this[0] = x;
3055     this.y = this.top = this.bottom = this[1] = y;
3056 };
3057
3058 Roo.lib.Point.prototype = new Roo.lib.Region();
3059 /*
3060  * Portions of this file are based on pieces of Yahoo User Interface Library
3061  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3062  * YUI licensed under the BSD License:
3063  * http://developer.yahoo.net/yui/license.txt
3064  * <script type="text/javascript">
3065  *
3066  */
3067  
3068 (function() {   
3069
3070     Roo.lib.Anim = {
3071         scroll : function(el, args, duration, easing, cb, scope) {
3072             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3073         },
3074
3075         motion : function(el, args, duration, easing, cb, scope) {
3076             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3077         },
3078
3079         color : function(el, args, duration, easing, cb, scope) {
3080             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3081         },
3082
3083         run : function(el, args, duration, easing, cb, scope, type) {
3084             type = type || Roo.lib.AnimBase;
3085             if (typeof easing == "string") {
3086                 easing = Roo.lib.Easing[easing];
3087             }
3088             var anim = new type(el, args, duration, easing);
3089             anim.animateX(function() {
3090                 Roo.callback(cb, scope);
3091             });
3092             return anim;
3093         }
3094     };
3095 })();/*
3096  * Portions of this file are based on pieces of Yahoo User Interface Library
3097  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3098  * YUI licensed under the BSD License:
3099  * http://developer.yahoo.net/yui/license.txt
3100  * <script type="text/javascript">
3101  *
3102  */
3103
3104 (function() {    
3105     var libFlyweight;
3106     
3107     function fly(el) {
3108         if (!libFlyweight) {
3109             libFlyweight = new Roo.Element.Flyweight();
3110         }
3111         libFlyweight.dom = el;
3112         return libFlyweight;
3113     }
3114
3115     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3116     
3117    
3118     
3119     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3120         if (el) {
3121             this.init(el, attributes, duration, method);
3122         }
3123     };
3124
3125     Roo.lib.AnimBase.fly = fly;
3126     
3127     
3128     
3129     Roo.lib.AnimBase.prototype = {
3130
3131         toString: function() {
3132             var el = this.getEl();
3133             var id = el.id || el.tagName;
3134             return ("Anim " + id);
3135         },
3136
3137         patterns: {
3138             noNegatives:        /width|height|opacity|padding/i,
3139             offsetAttribute:  /^((width|height)|(top|left))$/,
3140             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3141             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3142         },
3143
3144
3145         doMethod: function(attr, start, end) {
3146             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3147         },
3148
3149
3150         setAttribute: function(attr, val, unit) {
3151             if (this.patterns.noNegatives.test(attr)) {
3152                 val = (val > 0) ? val : 0;
3153             }
3154
3155             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3156         },
3157
3158
3159         getAttribute: function(attr) {
3160             var el = this.getEl();
3161             var val = fly(el).getStyle(attr);
3162
3163             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3164                 return parseFloat(val);
3165             }
3166
3167             var a = this.patterns.offsetAttribute.exec(attr) || [];
3168             var pos = !!( a[3] );
3169             var box = !!( a[2] );
3170
3171
3172             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3173                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3174             } else {
3175                 val = 0;
3176             }
3177
3178             return val;
3179         },
3180
3181
3182         getDefaultUnit: function(attr) {
3183             if (this.patterns.defaultUnit.test(attr)) {
3184                 return 'px';
3185             }
3186
3187             return '';
3188         },
3189
3190         animateX : function(callback, scope) {
3191             var f = function() {
3192                 this.onComplete.removeListener(f);
3193                 if (typeof callback == "function") {
3194                     callback.call(scope || this, this);
3195                 }
3196             };
3197             this.onComplete.addListener(f, this);
3198             this.animate();
3199         },
3200
3201
3202         setRuntimeAttribute: function(attr) {
3203             var start;
3204             var end;
3205             var attributes = this.attributes;
3206
3207             this.runtimeAttributes[attr] = {};
3208
3209             var isset = function(prop) {
3210                 return (typeof prop !== 'undefined');
3211             };
3212
3213             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3214                 return false;
3215             }
3216
3217             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3218
3219
3220             if (isset(attributes[attr]['to'])) {
3221                 end = attributes[attr]['to'];
3222             } else if (isset(attributes[attr]['by'])) {
3223                 if (start.constructor == Array) {
3224                     end = [];
3225                     for (var i = 0, len = start.length; i < len; ++i) {
3226                         end[i] = start[i] + attributes[attr]['by'][i];
3227                     }
3228                 } else {
3229                     end = start + attributes[attr]['by'];
3230                 }
3231             }
3232
3233             this.runtimeAttributes[attr].start = start;
3234             this.runtimeAttributes[attr].end = end;
3235
3236
3237             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3238         },
3239
3240
3241         init: function(el, attributes, duration, method) {
3242
3243             var isAnimated = false;
3244
3245
3246             var startTime = null;
3247
3248
3249             var actualFrames = 0;
3250
3251
3252             el = Roo.getDom(el);
3253
3254
3255             this.attributes = attributes || {};
3256
3257
3258             this.duration = duration || 1;
3259
3260
3261             this.method = method || Roo.lib.Easing.easeNone;
3262
3263
3264             this.useSeconds = true;
3265
3266
3267             this.currentFrame = 0;
3268
3269
3270             this.totalFrames = Roo.lib.AnimMgr.fps;
3271
3272
3273             this.getEl = function() {
3274                 return el;
3275             };
3276
3277
3278             this.isAnimated = function() {
3279                 return isAnimated;
3280             };
3281
3282
3283             this.getStartTime = function() {
3284                 return startTime;
3285             };
3286
3287             this.runtimeAttributes = {};
3288
3289
3290             this.animate = function() {
3291                 if (this.isAnimated()) {
3292                     return false;
3293                 }
3294
3295                 this.currentFrame = 0;
3296
3297                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3298
3299                 Roo.lib.AnimMgr.registerElement(this);
3300             };
3301
3302
3303             this.stop = function(finish) {
3304                 if (finish) {
3305                     this.currentFrame = this.totalFrames;
3306                     this._onTween.fire();
3307                 }
3308                 Roo.lib.AnimMgr.stop(this);
3309             };
3310
3311             var onStart = function() {
3312                 this.onStart.fire();
3313
3314                 this.runtimeAttributes = {};
3315                 for (var attr in this.attributes) {
3316                     this.setRuntimeAttribute(attr);
3317                 }
3318
3319                 isAnimated = true;
3320                 actualFrames = 0;
3321                 startTime = new Date();
3322             };
3323
3324
3325             var onTween = function() {
3326                 var data = {
3327                     duration: new Date() - this.getStartTime(),
3328                     currentFrame: this.currentFrame
3329                 };
3330
3331                 data.toString = function() {
3332                     return (
3333                             'duration: ' + data.duration +
3334                             ', currentFrame: ' + data.currentFrame
3335                             );
3336                 };
3337
3338                 this.onTween.fire(data);
3339
3340                 var runtimeAttributes = this.runtimeAttributes;
3341
3342                 for (var attr in runtimeAttributes) {
3343                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3344                 }
3345
3346                 actualFrames += 1;
3347             };
3348
3349             var onComplete = function() {
3350                 var actual_duration = (new Date() - startTime) / 1000 ;
3351
3352                 var data = {
3353                     duration: actual_duration,
3354                     frames: actualFrames,
3355                     fps: actualFrames / actual_duration
3356                 };
3357
3358                 data.toString = function() {
3359                     return (
3360                             'duration: ' + data.duration +
3361                             ', frames: ' + data.frames +
3362                             ', fps: ' + data.fps
3363                             );
3364                 };
3365
3366                 isAnimated = false;
3367                 actualFrames = 0;
3368                 this.onComplete.fire(data);
3369             };
3370
3371
3372             this._onStart = new Roo.util.Event(this);
3373             this.onStart = new Roo.util.Event(this);
3374             this.onTween = new Roo.util.Event(this);
3375             this._onTween = new Roo.util.Event(this);
3376             this.onComplete = new Roo.util.Event(this);
3377             this._onComplete = new Roo.util.Event(this);
3378             this._onStart.addListener(onStart);
3379             this._onTween.addListener(onTween);
3380             this._onComplete.addListener(onComplete);
3381         }
3382     };
3383 })();
3384 /*
3385  * Portions of this file are based on pieces of Yahoo User Interface Library
3386  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3387  * YUI licensed under the BSD License:
3388  * http://developer.yahoo.net/yui/license.txt
3389  * <script type="text/javascript">
3390  *
3391  */
3392
3393 Roo.lib.AnimMgr = new function() {
3394
3395         var thread = null;
3396
3397
3398         var queue = [];
3399
3400
3401         var tweenCount = 0;
3402
3403
3404         this.fps = 1000;
3405
3406
3407         this.delay = 1;
3408
3409
3410         this.registerElement = function(tween) {
3411             queue[queue.length] = tween;
3412             tweenCount += 1;
3413             tween._onStart.fire();
3414             this.start();
3415         };
3416
3417
3418         this.unRegister = function(tween, index) {
3419             tween._onComplete.fire();
3420             index = index || getIndex(tween);
3421             if (index != -1) {
3422                 queue.splice(index, 1);
3423             }
3424
3425             tweenCount -= 1;
3426             if (tweenCount <= 0) {
3427                 this.stop();
3428             }
3429         };
3430
3431
3432         this.start = function() {
3433             if (thread === null) {
3434                 thread = setInterval(this.run, this.delay);
3435             }
3436         };
3437
3438
3439         this.stop = function(tween) {
3440             if (!tween) {
3441                 clearInterval(thread);
3442
3443                 for (var i = 0, len = queue.length; i < len; ++i) {
3444                     if (queue[0].isAnimated()) {
3445                         this.unRegister(queue[0], 0);
3446                     }
3447                 }
3448
3449                 queue = [];
3450                 thread = null;
3451                 tweenCount = 0;
3452             }
3453             else {
3454                 this.unRegister(tween);
3455             }
3456         };
3457
3458
3459         this.run = function() {
3460             for (var i = 0, len = queue.length; i < len; ++i) {
3461                 var tween = queue[i];
3462                 if (!tween || !tween.isAnimated()) {
3463                     continue;
3464                 }
3465
3466                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3467                 {
3468                     tween.currentFrame += 1;
3469
3470                     if (tween.useSeconds) {
3471                         correctFrame(tween);
3472                     }
3473                     tween._onTween.fire();
3474                 }
3475                 else {
3476                     Roo.lib.AnimMgr.stop(tween, i);
3477                 }
3478             }
3479         };
3480
3481         var getIndex = function(anim) {
3482             for (var i = 0, len = queue.length; i < len; ++i) {
3483                 if (queue[i] == anim) {
3484                     return i;
3485                 }
3486             }
3487             return -1;
3488         };
3489
3490
3491         var correctFrame = function(tween) {
3492             var frames = tween.totalFrames;
3493             var frame = tween.currentFrame;
3494             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3495             var elapsed = (new Date() - tween.getStartTime());
3496             var tweak = 0;
3497
3498             if (elapsed < tween.duration * 1000) {
3499                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3500             } else {
3501                 tweak = frames - (frame + 1);
3502             }
3503             if (tweak > 0 && isFinite(tweak)) {
3504                 if (tween.currentFrame + tweak >= frames) {
3505                     tweak = frames - (frame + 1);
3506                 }
3507
3508                 tween.currentFrame += tweak;
3509             }
3510         };
3511     };/*
3512  * Portions of this file are based on pieces of Yahoo User Interface Library
3513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3514  * YUI licensed under the BSD License:
3515  * http://developer.yahoo.net/yui/license.txt
3516  * <script type="text/javascript">
3517  *
3518  */
3519 Roo.lib.Bezier = new function() {
3520
3521         this.getPosition = function(points, t) {
3522             var n = points.length;
3523             var tmp = [];
3524
3525             for (var i = 0; i < n; ++i) {
3526                 tmp[i] = [points[i][0], points[i][1]];
3527             }
3528
3529             for (var j = 1; j < n; ++j) {
3530                 for (i = 0; i < n - j; ++i) {
3531                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3532                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3533                 }
3534             }
3535
3536             return [ tmp[0][0], tmp[0][1] ];
3537
3538         };
3539     };/*
3540  * Portions of this file are based on pieces of Yahoo User Interface Library
3541  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3542  * YUI licensed under the BSD License:
3543  * http://developer.yahoo.net/yui/license.txt
3544  * <script type="text/javascript">
3545  *
3546  */
3547 (function() {
3548
3549     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3550         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3551     };
3552
3553     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3554
3555     var fly = Roo.lib.AnimBase.fly;
3556     var Y = Roo.lib;
3557     var superclass = Y.ColorAnim.superclass;
3558     var proto = Y.ColorAnim.prototype;
3559
3560     proto.toString = function() {
3561         var el = this.getEl();
3562         var id = el.id || el.tagName;
3563         return ("ColorAnim " + id);
3564     };
3565
3566     proto.patterns.color = /color$/i;
3567     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3568     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3569     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3570     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3571
3572
3573     proto.parseColor = function(s) {
3574         if (s.length == 3) {
3575             return s;
3576         }
3577
3578         var c = this.patterns.hex.exec(s);
3579         if (c && c.length == 4) {
3580             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3581         }
3582
3583         c = this.patterns.rgb.exec(s);
3584         if (c && c.length == 4) {
3585             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3586         }
3587
3588         c = this.patterns.hex3.exec(s);
3589         if (c && c.length == 4) {
3590             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3591         }
3592
3593         return null;
3594     };
3595     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3596     proto.getAttribute = function(attr) {
3597         var el = this.getEl();
3598         if (this.patterns.color.test(attr)) {
3599             var val = fly(el).getStyle(attr);
3600
3601             if (this.patterns.transparent.test(val)) {
3602                 var parent = el.parentNode;
3603                 val = fly(parent).getStyle(attr);
3604
3605                 while (parent && this.patterns.transparent.test(val)) {
3606                     parent = parent.parentNode;
3607                     val = fly(parent).getStyle(attr);
3608                     if (parent.tagName.toUpperCase() == 'HTML') {
3609                         val = '#fff';
3610                     }
3611                 }
3612             }
3613         } else {
3614             val = superclass.getAttribute.call(this, attr);
3615         }
3616
3617         return val;
3618     };
3619     proto.getAttribute = function(attr) {
3620         var el = this.getEl();
3621         if (this.patterns.color.test(attr)) {
3622             var val = fly(el).getStyle(attr);
3623
3624             if (this.patterns.transparent.test(val)) {
3625                 var parent = el.parentNode;
3626                 val = fly(parent).getStyle(attr);
3627
3628                 while (parent && this.patterns.transparent.test(val)) {
3629                     parent = parent.parentNode;
3630                     val = fly(parent).getStyle(attr);
3631                     if (parent.tagName.toUpperCase() == 'HTML') {
3632                         val = '#fff';
3633                     }
3634                 }
3635             }
3636         } else {
3637             val = superclass.getAttribute.call(this, attr);
3638         }
3639
3640         return val;
3641     };
3642
3643     proto.doMethod = function(attr, start, end) {
3644         var val;
3645
3646         if (this.patterns.color.test(attr)) {
3647             val = [];
3648             for (var i = 0, len = start.length; i < len; ++i) {
3649                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3650             }
3651
3652             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3653         }
3654         else {
3655             val = superclass.doMethod.call(this, attr, start, end);
3656         }
3657
3658         return val;
3659     };
3660
3661     proto.setRuntimeAttribute = function(attr) {
3662         superclass.setRuntimeAttribute.call(this, attr);
3663
3664         if (this.patterns.color.test(attr)) {
3665             var attributes = this.attributes;
3666             var start = this.parseColor(this.runtimeAttributes[attr].start);
3667             var end = this.parseColor(this.runtimeAttributes[attr].end);
3668
3669             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3670                 end = this.parseColor(attributes[attr].by);
3671
3672                 for (var i = 0, len = start.length; i < len; ++i) {
3673                     end[i] = start[i] + end[i];
3674                 }
3675             }
3676
3677             this.runtimeAttributes[attr].start = start;
3678             this.runtimeAttributes[attr].end = end;
3679         }
3680     };
3681 })();
3682
3683 /*
3684  * Portions of this file are based on pieces of Yahoo User Interface Library
3685  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3686  * YUI licensed under the BSD License:
3687  * http://developer.yahoo.net/yui/license.txt
3688  * <script type="text/javascript">
3689  *
3690  */
3691 Roo.lib.Easing = {
3692
3693
3694     easeNone: function (t, b, c, d) {
3695         return c * t / d + b;
3696     },
3697
3698
3699     easeIn: function (t, b, c, d) {
3700         return c * (t /= d) * t + b;
3701     },
3702
3703
3704     easeOut: function (t, b, c, d) {
3705         return -c * (t /= d) * (t - 2) + b;
3706     },
3707
3708
3709     easeBoth: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t + b;
3712         }
3713
3714         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3715     },
3716
3717
3718     easeInStrong: function (t, b, c, d) {
3719         return c * (t /= d) * t * t * t + b;
3720     },
3721
3722
3723     easeOutStrong: function (t, b, c, d) {
3724         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3725     },
3726
3727
3728     easeBothStrong: function (t, b, c, d) {
3729         if ((t /= d / 2) < 1) {
3730             return c / 2 * t * t * t * t + b;
3731         }
3732
3733         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3734     },
3735
3736
3737
3738     elasticIn: function (t, b, c, d, a, p) {
3739         if (t == 0) {
3740             return b;
3741         }
3742         if ((t /= d) == 1) {
3743             return b + c;
3744         }
3745         if (!p) {
3746             p = d * .3;
3747         }
3748
3749         if (!a || a < Math.abs(c)) {
3750             a = c;
3751             var s = p / 4;
3752         }
3753         else {
3754             var s = p / (2 * Math.PI) * Math.asin(c / a);
3755         }
3756
3757         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3758     },
3759
3760
3761     elasticOut: function (t, b, c, d, a, p) {
3762         if (t == 0) {
3763             return b;
3764         }
3765         if ((t /= d) == 1) {
3766             return b + c;
3767         }
3768         if (!p) {
3769             p = d * .3;
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3781     },
3782
3783
3784     elasticBoth: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788
3789         if ((t /= d / 2) == 2) {
3790             return b + c;
3791         }
3792
3793         if (!p) {
3794             p = d * (.3 * 1.5);
3795         }
3796
3797         if (!a || a < Math.abs(c)) {
3798             a = c;
3799             var s = p / 4;
3800         }
3801         else {
3802             var s = p / (2 * Math.PI) * Math.asin(c / a);
3803         }
3804
3805         if (t < 1) {
3806             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3807                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3808         }
3809         return a * Math.pow(2, -10 * (t -= 1)) *
3810                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3811     },
3812
3813
3814
3815     backIn: function (t, b, c, d, s) {
3816         if (typeof s == 'undefined') {
3817             s = 1.70158;
3818         }
3819         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3820     },
3821
3822
3823     backOut: function (t, b, c, d, s) {
3824         if (typeof s == 'undefined') {
3825             s = 1.70158;
3826         }
3827         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3828     },
3829
3830
3831     backBoth: function (t, b, c, d, s) {
3832         if (typeof s == 'undefined') {
3833             s = 1.70158;
3834         }
3835
3836         if ((t /= d / 2 ) < 1) {
3837             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3838         }
3839         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3840     },
3841
3842
3843     bounceIn: function (t, b, c, d) {
3844         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3845     },
3846
3847
3848     bounceOut: function (t, b, c, d) {
3849         if ((t /= d) < (1 / 2.75)) {
3850             return c * (7.5625 * t * t) + b;
3851         } else if (t < (2 / 2.75)) {
3852             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3853         } else if (t < (2.5 / 2.75)) {
3854             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3855         }
3856         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3857     },
3858
3859
3860     bounceBoth: function (t, b, c, d) {
3861         if (t < d / 2) {
3862             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3863         }
3864         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3865     }
3866 };/*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Motion = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Motion.superclass;
3886         var proto = Y.Motion.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Motion " + id);
3892         };
3893
3894         proto.patterns.points = /^points$/i;
3895
3896         proto.setAttribute = function(attr, val, unit) {
3897             if (this.patterns.points.test(attr)) {
3898                 unit = unit || 'px';
3899                 superclass.setAttribute.call(this, 'left', val[0], unit);
3900                 superclass.setAttribute.call(this, 'top', val[1], unit);
3901             } else {
3902                 superclass.setAttribute.call(this, attr, val, unit);
3903             }
3904         };
3905
3906         proto.getAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var val = [
3909                         superclass.getAttribute.call(this, 'left'),
3910                         superclass.getAttribute.call(this, 'top')
3911                         ];
3912             } else {
3913                 val = superclass.getAttribute.call(this, attr);
3914             }
3915
3916             return val;
3917         };
3918
3919         proto.doMethod = function(attr, start, end) {
3920             var val = null;
3921
3922             if (this.patterns.points.test(attr)) {
3923                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3924                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3925             } else {
3926                 val = superclass.doMethod.call(this, attr, start, end);
3927             }
3928             return val;
3929         };
3930
3931         proto.setRuntimeAttribute = function(attr) {
3932             if (this.patterns.points.test(attr)) {
3933                 var el = this.getEl();
3934                 var attributes = this.attributes;
3935                 var start;
3936                 var control = attributes['points']['control'] || [];
3937                 var end;
3938                 var i, len;
3939
3940                 if (control.length > 0 && !(control[0] instanceof Array)) {
3941                     control = [control];
3942                 } else {
3943                     var tmp = [];
3944                     for (i = 0,len = control.length; i < len; ++i) {
3945                         tmp[i] = control[i];
3946                     }
3947                     control = tmp;
3948                 }
3949
3950                 Roo.fly(el).position();
3951
3952                 if (isset(attributes['points']['from'])) {
3953                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3954                 }
3955                 else {
3956                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3957                 }
3958
3959                 start = this.getAttribute('points');
3960
3961
3962                 if (isset(attributes['points']['to'])) {
3963                     end = translateValues.call(this, attributes['points']['to'], start);
3964
3965                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3966                     for (i = 0,len = control.length; i < len; ++i) {
3967                         control[i] = translateValues.call(this, control[i], start);
3968                     }
3969
3970
3971                 } else if (isset(attributes['points']['by'])) {
3972                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3973
3974                     for (i = 0,len = control.length; i < len; ++i) {
3975                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3976                     }
3977                 }
3978
3979                 this.runtimeAttributes[attr] = [start];
3980
3981                 if (control.length > 0) {
3982                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3983                 }
3984
3985                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3986             }
3987             else {
3988                 superclass.setRuntimeAttribute.call(this, attr);
3989             }
3990         };
3991
3992         var translateValues = function(val, start) {
3993             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3994             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3995
3996             return val;
3997         };
3998
3999         var isset = function(prop) {
4000             return (typeof prop !== 'undefined');
4001         };
4002     })();
4003 /*
4004  * Portions of this file are based on pieces of Yahoo User Interface Library
4005  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4006  * YUI licensed under the BSD License:
4007  * http://developer.yahoo.net/yui/license.txt
4008  * <script type="text/javascript">
4009  *
4010  */
4011     (function() {
4012         Roo.lib.Scroll = function(el, attributes, duration, method) {
4013             if (el) {
4014                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4015             }
4016         };
4017
4018         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4019
4020
4021         var Y = Roo.lib;
4022         var superclass = Y.Scroll.superclass;
4023         var proto = Y.Scroll.prototype;
4024
4025         proto.toString = function() {
4026             var el = this.getEl();
4027             var id = el.id || el.tagName;
4028             return ("Scroll " + id);
4029         };
4030
4031         proto.doMethod = function(attr, start, end) {
4032             var val = null;
4033
4034             if (attr == 'scroll') {
4035                 val = [
4036                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4037                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4038                         ];
4039
4040             } else {
4041                 val = superclass.doMethod.call(this, attr, start, end);
4042             }
4043             return val;
4044         };
4045
4046         proto.getAttribute = function(attr) {
4047             var val = null;
4048             var el = this.getEl();
4049
4050             if (attr == 'scroll') {
4051                 val = [ el.scrollLeft, el.scrollTop ];
4052             } else {
4053                 val = superclass.getAttribute.call(this, attr);
4054             }
4055
4056             return val;
4057         };
4058
4059         proto.setAttribute = function(attr, val, unit) {
4060             var el = this.getEl();
4061
4062             if (attr == 'scroll') {
4063                 el.scrollLeft = val[0];
4064                 el.scrollTop = val[1];
4065             } else {
4066                 superclass.setAttribute.call(this, attr, val, unit);
4067             }
4068         };
4069     })();
4070 /*
4071  * Based on:
4072  * Ext JS Library 1.1.1
4073  * Copyright(c) 2006-2007, Ext JS, LLC.
4074  *
4075  * Originally Released Under LGPL - original licence link has changed is not relivant.
4076  *
4077  * Fork - LGPL
4078  * <script type="text/javascript">
4079  */
4080
4081
4082 // nasty IE9 hack - what a pile of crap that is..
4083
4084  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4085     Range.prototype.createContextualFragment = function (html) {
4086         var doc = window.document;
4087         var container = doc.createElement("div");
4088         container.innerHTML = html;
4089         var frag = doc.createDocumentFragment(), n;
4090         while ((n = container.firstChild)) {
4091             frag.appendChild(n);
4092         }
4093         return frag;
4094     };
4095 }
4096
4097 /**
4098  * @class Roo.DomHelper
4099  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4100  * 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>.
4101  * @singleton
4102  */
4103 Roo.DomHelper = function(){
4104     var tempTableEl = null;
4105     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4106     var tableRe = /^table|tbody|tr|td$/i;
4107     var xmlns = {};
4108     // build as innerHTML where available
4109     /** @ignore */
4110     var createHtml = function(o){
4111         if(typeof o == 'string'){
4112             return o;
4113         }
4114         var b = "";
4115         if(!o.tag){
4116             o.tag = "div";
4117         }
4118         b += "<" + o.tag;
4119         for(var attr in o){
4120             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4121             if(attr == "style"){
4122                 var s = o["style"];
4123                 if(typeof s == "function"){
4124                     s = s.call();
4125                 }
4126                 if(typeof s == "string"){
4127                     b += ' style="' + s + '"';
4128                 }else if(typeof s == "object"){
4129                     b += ' style="';
4130                     for(var key in s){
4131                         if(typeof s[key] != "function"){
4132                             b += key + ":" + s[key] + ";";
4133                         }
4134                     }
4135                     b += '"';
4136                 }
4137             }else{
4138                 if(attr == "cls"){
4139                     b += ' class="' + o["cls"] + '"';
4140                 }else if(attr == "htmlFor"){
4141                     b += ' for="' + o["htmlFor"] + '"';
4142                 }else{
4143                     b += " " + attr + '="' + o[attr] + '"';
4144                 }
4145             }
4146         }
4147         if(emptyTags.test(o.tag)){
4148             b += "/>";
4149         }else{
4150             b += ">";
4151             var cn = o.children || o.cn;
4152             if(cn){
4153                 //http://bugs.kde.org/show_bug.cgi?id=71506
4154                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4155                     for(var i = 0, len = cn.length; i < len; i++) {
4156                         b += createHtml(cn[i], b);
4157                     }
4158                 }else{
4159                     b += createHtml(cn, b);
4160                 }
4161             }
4162             if(o.html){
4163                 b += o.html;
4164             }
4165             b += "</" + o.tag + ">";
4166         }
4167         return b;
4168     };
4169
4170     // build as dom
4171     /** @ignore */
4172     var createDom = function(o, parentNode){
4173          
4174         // defininition craeted..
4175         var ns = false;
4176         if (o.ns && o.ns != 'html') {
4177                
4178             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4179                 xmlns[o.ns] = o.xmlns;
4180                 ns = o.xmlns;
4181             }
4182             if (typeof(xmlns[o.ns]) == 'undefined') {
4183                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4184             }
4185             ns = xmlns[o.ns];
4186         }
4187         
4188         
4189         if (typeof(o) == 'string') {
4190             return parentNode.appendChild(document.createTextNode(o));
4191         }
4192         o.tag = o.tag || div;
4193         if (o.ns && Roo.isIE) {
4194             ns = false;
4195             o.tag = o.ns + ':' + o.tag;
4196             
4197         }
4198         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4199         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4200         for(var attr in o){
4201             
4202             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4203                     attr == "style" || typeof o[attr] == "function") continue;
4204                     
4205             if(attr=="cls" && Roo.isIE){
4206                 el.className = o["cls"];
4207             }else{
4208                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4209                 else el[attr] = o[attr];
4210             }
4211         }
4212         Roo.DomHelper.applyStyles(el, o.style);
4213         var cn = o.children || o.cn;
4214         if(cn){
4215             //http://bugs.kde.org/show_bug.cgi?id=71506
4216              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4217                 for(var i = 0, len = cn.length; i < len; i++) {
4218                     createDom(cn[i], el);
4219                 }
4220             }else{
4221                 createDom(cn, el);
4222             }
4223         }
4224         if(o.html){
4225             el.innerHTML = o.html;
4226         }
4227         if(parentNode){
4228            parentNode.appendChild(el);
4229         }
4230         return el;
4231     };
4232
4233     var ieTable = function(depth, s, h, e){
4234         tempTableEl.innerHTML = [s, h, e].join('');
4235         var i = -1, el = tempTableEl;
4236         while(++i < depth){
4237             el = el.firstChild;
4238         }
4239         return el;
4240     };
4241
4242     // kill repeat to save bytes
4243     var ts = '<table>',
4244         te = '</table>',
4245         tbs = ts+'<tbody>',
4246         tbe = '</tbody>'+te,
4247         trs = tbs + '<tr>',
4248         tre = '</tr>'+tbe;
4249
4250     /**
4251      * @ignore
4252      * Nasty code for IE's broken table implementation
4253      */
4254     var insertIntoTable = function(tag, where, el, html){
4255         if(!tempTableEl){
4256             tempTableEl = document.createElement('div');
4257         }
4258         var node;
4259         var before = null;
4260         if(tag == 'td'){
4261             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4262                 return;
4263             }
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267             } else{
4268                 before = el.nextSibling;
4269                 el = el.parentNode;
4270             }
4271             node = ieTable(4, trs, html, tre);
4272         }
4273         else if(tag == 'tr'){
4274             if(where == 'beforebegin'){
4275                 before = el;
4276                 el = el.parentNode;
4277                 node = ieTable(3, tbs, html, tbe);
4278             } else if(where == 'afterend'){
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281                 node = ieTable(3, tbs, html, tbe);
4282             } else{ // INTO a TR
4283                 if(where == 'afterbegin'){
4284                     before = el.firstChild;
4285                 }
4286                 node = ieTable(4, trs, html, tre);
4287             }
4288         } else if(tag == 'tbody'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(2, ts, html, te);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(2, ts, html, te);
4297             } else{
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(3, tbs, html, tbe);
4302             }
4303         } else{ // TABLE
4304             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4305                 return;
4306             }
4307             if(where == 'afterbegin'){
4308                 before = el.firstChild;
4309             }
4310             node = ieTable(2, ts, html, te);
4311         }
4312         el.insertBefore(node, before);
4313         return node;
4314     };
4315
4316     return {
4317     /** True to force the use of DOM instead of html fragments @type Boolean */
4318     useDom : false,
4319
4320     /**
4321      * Returns the markup for the passed Element(s) config
4322      * @param {Object} o The Dom object spec (and children)
4323      * @return {String}
4324      */
4325     markup : function(o){
4326         return createHtml(o);
4327     },
4328
4329     /**
4330      * Applies a style specification to an element
4331      * @param {String/HTMLElement} el The element to apply styles to
4332      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4333      * a function which returns such a specification.
4334      */
4335     applyStyles : function(el, styles){
4336         if(styles){
4337            el = Roo.fly(el);
4338            if(typeof styles == "string"){
4339                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4340                var matches;
4341                while ((matches = re.exec(styles)) != null){
4342                    el.setStyle(matches[1], matches[2]);
4343                }
4344            }else if (typeof styles == "object"){
4345                for (var style in styles){
4346                   el.setStyle(style, styles[style]);
4347                }
4348            }else if (typeof styles == "function"){
4349                 Roo.DomHelper.applyStyles(el, styles.call());
4350            }
4351         }
4352     },
4353
4354     /**
4355      * Inserts an HTML fragment into the Dom
4356      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4357      * @param {HTMLElement} el The context element
4358      * @param {String} html The HTML fragmenet
4359      * @return {HTMLElement} The new node
4360      */
4361     insertHtml : function(where, el, html){
4362         where = where.toLowerCase();
4363         if(el.insertAdjacentHTML){
4364             if(tableRe.test(el.tagName)){
4365                 var rs;
4366                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4367                     return rs;
4368                 }
4369             }
4370             switch(where){
4371                 case "beforebegin":
4372                     el.insertAdjacentHTML('BeforeBegin', html);
4373                     return el.previousSibling;
4374                 case "afterbegin":
4375                     el.insertAdjacentHTML('AfterBegin', html);
4376                     return el.firstChild;
4377                 case "beforeend":
4378                     el.insertAdjacentHTML('BeforeEnd', html);
4379                     return el.lastChild;
4380                 case "afterend":
4381                     el.insertAdjacentHTML('AfterEnd', html);
4382                     return el.nextSibling;
4383             }
4384             throw 'Illegal insertion point -> "' + where + '"';
4385         }
4386         var range = el.ownerDocument.createRange();
4387         var frag;
4388         switch(where){
4389              case "beforebegin":
4390                 range.setStartBefore(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el);
4393                 return el.previousSibling;
4394              case "afterbegin":
4395                 if(el.firstChild){
4396                     range.setStartBefore(el.firstChild);
4397                     frag = range.createContextualFragment(html);
4398                     el.insertBefore(frag, el.firstChild);
4399                     return el.firstChild;
4400                 }else{
4401                     el.innerHTML = html;
4402                     return el.firstChild;
4403                 }
4404             case "beforeend":
4405                 if(el.lastChild){
4406                     range.setStartAfter(el.lastChild);
4407                     frag = range.createContextualFragment(html);
4408                     el.appendChild(frag);
4409                     return el.lastChild;
4410                 }else{
4411                     el.innerHTML = html;
4412                     return el.lastChild;
4413                 }
4414             case "afterend":
4415                 range.setStartAfter(el);
4416                 frag = range.createContextualFragment(html);
4417                 el.parentNode.insertBefore(frag, el.nextSibling);
4418                 return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421     },
4422
4423     /**
4424      * Creates new Dom element(s) and inserts them before el
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     insertBefore : function(el, o, returnElement){
4431         return this.doInsert(el, o, returnElement, "beforeBegin");
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them after el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object} o The Dom object spec (and children)
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertAfter : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them as the first child of el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertFirst : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterBegin");
4454     },
4455
4456     // private
4457     doInsert : function(el, o, returnElement, pos, sibling){
4458         el = Roo.getDom(el);
4459         var newNode;
4460         if(this.useDom || o.ns){
4461             newNode = createDom(o, null);
4462             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4463         }else{
4464             var html = createHtml(o);
4465             newNode = this.insertHtml(pos, el, html);
4466         }
4467         return returnElement ? Roo.get(newNode, true) : newNode;
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and appends them to el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     append : function(el, o, returnElement){
4478         el = Roo.getDom(el);
4479         var newNode;
4480         if(this.useDom || o.ns){
4481             newNode = createDom(o, null);
4482             el.appendChild(newNode);
4483         }else{
4484             var html = createHtml(o);
4485             newNode = this.insertHtml("beforeEnd", el, html);
4486         }
4487         return returnElement ? Roo.get(newNode, true) : newNode;
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and overwrites the contents of el with them
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     overwrite : function(el, o, returnElement){
4498         el = Roo.getDom(el);
4499         if (o.ns) {
4500           
4501             while (el.childNodes.length) {
4502                 el.removeChild(el.firstChild);
4503             }
4504             createDom(o, el);
4505         } else {
4506             el.innerHTML = createHtml(o);   
4507         }
4508         
4509         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4510     },
4511
4512     /**
4513      * Creates a new Roo.DomHelper.Template from the Dom object spec
4514      * @param {Object} o The Dom object spec (and children)
4515      * @return {Roo.DomHelper.Template} The new template
4516      */
4517     createTemplate : function(o){
4518         var html = createHtml(o);
4519         return new Roo.Template(html);
4520     }
4521     };
4522 }();
4523 /*
4524  * Based on:
4525  * Ext JS Library 1.1.1
4526  * Copyright(c) 2006-2007, Ext JS, LLC.
4527  *
4528  * Originally Released Under LGPL - original licence link has changed is not relivant.
4529  *
4530  * Fork - LGPL
4531  * <script type="text/javascript">
4532  */
4533  
4534 /**
4535 * @class Roo.Template
4536 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4537 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4538 * Usage:
4539 <pre><code>
4540 var t = new Roo.Template({
4541     html :  '&lt;div name="{id}"&gt;' + 
4542         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4543         '&lt;/div&gt;',
4544     myformat: function (value, allValues) {
4545         return 'XX' + value;
4546     }
4547 });
4548 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4549 </code></pre>
4550 * For more information see this blog post with examples:
4551 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4552      - Create Elements using DOM, HTML fragments and Templates</a>. 
4553 * @constructor
4554 * @param {Object} cfg - Configuration object.
4555 */
4556 Roo.Template = function(cfg){
4557     // BC!
4558     if(cfg instanceof Array){
4559         cfg = cfg.join("");
4560     }else if(arguments.length > 1){
4561         cfg = Array.prototype.join.call(arguments, "");
4562     }
4563     
4564     
4565     if (typeof(cfg) == 'object') {
4566         Roo.apply(this,cfg)
4567     } else {
4568         // bc
4569         this.html = cfg;
4570     }
4571     if (this.url) {
4572         this.load();
4573     }
4574     
4575 };
4576 Roo.Template.prototype = {
4577     
4578     /**
4579      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4580      *                    it should be fixed so that template is observable...
4581      */
4582     url : false,
4583     /**
4584      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4585      */
4586     html : '',
4587     /**
4588      * Returns an HTML fragment of this template with the specified values applied.
4589      * @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'})
4590      * @return {String} The HTML fragment
4591      */
4592     applyTemplate : function(values){
4593         try {
4594            
4595             if(this.compiled){
4596                 return this.compiled(values);
4597             }
4598             var useF = this.disableFormats !== true;
4599             var fm = Roo.util.Format, tpl = this;
4600             var fn = function(m, name, format, args){
4601                 if(format && useF){
4602                     if(format.substr(0, 5) == "this."){
4603                         return tpl.call(format.substr(5), values[name], values);
4604                     }else{
4605                         if(args){
4606                             // quoted values are required for strings in compiled templates, 
4607                             // but for non compiled we need to strip them
4608                             // quoted reversed for jsmin
4609                             var re = /^\s*['"](.*)["']\s*$/;
4610                             args = args.split(',');
4611                             for(var i = 0, len = args.length; i < len; i++){
4612                                 args[i] = args[i].replace(re, "$1");
4613                             }
4614                             args = [values[name]].concat(args);
4615                         }else{
4616                             args = [values[name]];
4617                         }
4618                         return fm[format].apply(fm, args);
4619                     }
4620                 }else{
4621                     return values[name] !== undefined ? values[name] : "";
4622                 }
4623             };
4624             return this.html.replace(this.re, fn);
4625         } catch (e) {
4626             Roo.log(e);
4627             throw e;
4628         }
4629          
4630     },
4631     
4632     loading : false,
4633       
4634     load : function ()
4635     {
4636          
4637         if (this.loading) {
4638             return;
4639         }
4640         var _t = this;
4641         
4642         this.loading = true;
4643         this.compiled = false;
4644         
4645         var cx = new Roo.data.Connection();
4646         cx.request({
4647             url : this.url,
4648             method : 'GET',
4649             success : function (response) {
4650                 _t.loading = false;
4651                 _t.html = response.responseText;
4652                 _t.url = false;
4653                 _t.compile();
4654              },
4655             failure : function(response) {
4656                 Roo.log("Template failed to load from " + url);
4657                 _t.loading = false;
4658             }
4659         });
4660     },
4661
4662     /**
4663      * Sets the HTML used as the template and optionally compiles it.
4664      * @param {String} html
4665      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4666      * @return {Roo.Template} this
4667      */
4668     set : function(html, compile){
4669         this.html = html;
4670         this.compiled = null;
4671         if(compile){
4672             this.compile();
4673         }
4674         return this;
4675     },
4676     
4677     /**
4678      * True to disable format functions (defaults to false)
4679      * @type Boolean
4680      */
4681     disableFormats : false,
4682     
4683     /**
4684     * The regular expression used to match template variables 
4685     * @type RegExp
4686     * @property 
4687     */
4688     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4689     
4690     /**
4691      * Compiles the template into an internal function, eliminating the RegEx overhead.
4692      * @return {Roo.Template} this
4693      */
4694     compile : function(){
4695         var fm = Roo.util.Format;
4696         var useF = this.disableFormats !== true;
4697         var sep = Roo.isGecko ? "+" : ",";
4698         var fn = function(m, name, format, args){
4699             if(format && useF){
4700                 args = args ? ',' + args : "";
4701                 if(format.substr(0, 5) != "this."){
4702                     format = "fm." + format + '(';
4703                 }else{
4704                     format = 'this.call("'+ format.substr(5) + '", ';
4705                     args = ", values";
4706                 }
4707             }else{
4708                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4709             }
4710             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4711         };
4712         var body;
4713         // branched to use + in gecko and [].join() in others
4714         if(Roo.isGecko){
4715             body = "this.compiled = function(values){ return '" +
4716                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4717                     "';};";
4718         }else{
4719             body = ["this.compiled = function(values){ return ['"];
4720             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4721             body.push("'].join('');};");
4722             body = body.join('');
4723         }
4724         /**
4725          * eval:var:values
4726          * eval:var:fm
4727          */
4728         eval(body);
4729         return this;
4730     },
4731     
4732     // private function used to call members
4733     call : function(fnName, value, allValues){
4734         return this[fnName](value, allValues);
4735     },
4736     
4737     /**
4738      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4739      * @param {String/HTMLElement/Roo.Element} el The context element
4740      * @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'})
4741      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4742      * @return {HTMLElement/Roo.Element} The new node or Element
4743      */
4744     insertFirst: function(el, values, returnElement){
4745         return this.doInsert('afterBegin', el, values, returnElement);
4746     },
4747
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) before el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @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'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertBefore: function(el, values, returnElement){
4756         return this.doInsert('beforeBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) after el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @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'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertAfter : function(el, values, returnElement){
4767         return this.doInsert('afterEnd', el, values, returnElement);
4768     },
4769     
4770     /**
4771      * Applies the supplied values to the template and appends the new node(s) to el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @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'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     append : function(el, values, returnElement){
4778         return this.doInsert('beforeEnd', el, values, returnElement);
4779     },
4780
4781     doInsert : function(where, el, values, returnEl){
4782         el = Roo.getDom(el);
4783         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4784         return returnEl ? Roo.get(newNode, true) : newNode;
4785     },
4786
4787     /**
4788      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4789      * @param {String/HTMLElement/Roo.Element} el The context element
4790      * @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'})
4791      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4792      * @return {HTMLElement/Roo.Element} The new node or Element
4793      */
4794     overwrite : function(el, values, returnElement){
4795         el = Roo.getDom(el);
4796         el.innerHTML = this.applyTemplate(values);
4797         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4798     }
4799 };
4800 /**
4801  * Alias for {@link #applyTemplate}
4802  * @method
4803  */
4804 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4805
4806 // backwards compat
4807 Roo.DomHelper.Template = Roo.Template;
4808
4809 /**
4810  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4811  * @param {String/HTMLElement} el A DOM element or its id
4812  * @returns {Roo.Template} The created template
4813  * @static
4814  */
4815 Roo.Template.from = function(el){
4816     el = Roo.getDom(el);
4817     return new Roo.Template(el.value || el.innerHTML);
4818 };/*
4819  * Based on:
4820  * Ext JS Library 1.1.1
4821  * Copyright(c) 2006-2007, Ext JS, LLC.
4822  *
4823  * Originally Released Under LGPL - original licence link has changed is not relivant.
4824  *
4825  * Fork - LGPL
4826  * <script type="text/javascript">
4827  */
4828  
4829
4830 /*
4831  * This is code is also distributed under MIT license for use
4832  * with jQuery and prototype JavaScript libraries.
4833  */
4834 /**
4835  * @class Roo.DomQuery
4836 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).
4837 <p>
4838 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>
4839
4840 <p>
4841 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.
4842 </p>
4843 <h4>Element Selectors:</h4>
4844 <ul class="list">
4845     <li> <b>*</b> any element</li>
4846     <li> <b>E</b> an element with the tag E</li>
4847     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4848     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4849     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4850     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4851 </ul>
4852 <h4>Attribute Selectors:</h4>
4853 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4854 <ul class="list">
4855     <li> <b>E[foo]</b> has an attribute "foo"</li>
4856     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4857     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4858     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4859     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4860     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4861     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4862 </ul>
4863 <h4>Pseudo Classes:</h4>
4864 <ul class="list">
4865     <li> <b>E:first-child</b> E is the first child of its parent</li>
4866     <li> <b>E:last-child</b> E is the last child of its parent</li>
4867     <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>
4868     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4869     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4870     <li> <b>E:only-child</b> E is the only child of its parent</li>
4871     <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>
4872     <li> <b>E:first</b> the first E in the resultset</li>
4873     <li> <b>E:last</b> the last E in the resultset</li>
4874     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4875     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4876     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4877     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4878     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4879     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4880     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4881     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4882     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4883 </ul>
4884 <h4>CSS Value Selectors:</h4>
4885 <ul class="list">
4886     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4887     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4888     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4889     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4890     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4891     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4892 </ul>
4893  * @singleton
4894  */
4895 Roo.DomQuery = function(){
4896     var cache = {}, simpleCache = {}, valueCache = {};
4897     var nonSpace = /\S/;
4898     var trimRe = /^\s+|\s+$/g;
4899     var tplRe = /\{(\d+)\}/g;
4900     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4901     var tagTokenRe = /^(#)?([\w-\*]+)/;
4902     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4903
4904     function child(p, index){
4905         var i = 0;
4906         var n = p.firstChild;
4907         while(n){
4908             if(n.nodeType == 1){
4909                if(++i == index){
4910                    return n;
4911                }
4912             }
4913             n = n.nextSibling;
4914         }
4915         return null;
4916     };
4917
4918     function next(n){
4919         while((n = n.nextSibling) && n.nodeType != 1);
4920         return n;
4921     };
4922
4923     function prev(n){
4924         while((n = n.previousSibling) && n.nodeType != 1);
4925         return n;
4926     };
4927
4928     function children(d){
4929         var n = d.firstChild, ni = -1;
4930             while(n){
4931                 var nx = n.nextSibling;
4932                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4933                     d.removeChild(n);
4934                 }else{
4935                     n.nodeIndex = ++ni;
4936                 }
4937                 n = nx;
4938             }
4939             return this;
4940         };
4941
4942     function byClassName(c, a, v){
4943         if(!v){
4944             return c;
4945         }
4946         var r = [], ri = -1, cn;
4947         for(var i = 0, ci; ci = c[i]; i++){
4948             if((' '+ci.className+' ').indexOf(v) != -1){
4949                 r[++ri] = ci;
4950             }
4951         }
4952         return r;
4953     };
4954
4955     function attrValue(n, attr){
4956         if(!n.tagName && typeof n.length != "undefined"){
4957             n = n[0];
4958         }
4959         if(!n){
4960             return null;
4961         }
4962         if(attr == "for"){
4963             return n.htmlFor;
4964         }
4965         if(attr == "class" || attr == "className"){
4966             return n.className;
4967         }
4968         return n.getAttribute(attr) || n[attr];
4969
4970     };
4971
4972     function getNodes(ns, mode, tagName){
4973         var result = [], ri = -1, cs;
4974         if(!ns){
4975             return result;
4976         }
4977         tagName = tagName || "*";
4978         if(typeof ns.getElementsByTagName != "undefined"){
4979             ns = [ns];
4980         }
4981         if(!mode){
4982             for(var i = 0, ni; ni = ns[i]; i++){
4983                 cs = ni.getElementsByTagName(tagName);
4984                 for(var j = 0, ci; ci = cs[j]; j++){
4985                     result[++ri] = ci;
4986                 }
4987             }
4988         }else if(mode == "/" || mode == ">"){
4989             var utag = tagName.toUpperCase();
4990             for(var i = 0, ni, cn; ni = ns[i]; i++){
4991                 cn = ni.children || ni.childNodes;
4992                 for(var j = 0, cj; cj = cn[j]; j++){
4993                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4994                         result[++ri] = cj;
4995                     }
4996                 }
4997             }
4998         }else if(mode == "+"){
4999             var utag = tagName.toUpperCase();
5000             for(var i = 0, n; n = ns[i]; i++){
5001                 while((n = n.nextSibling) && n.nodeType != 1);
5002                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5003                     result[++ri] = n;
5004                 }
5005             }
5006         }else if(mode == "~"){
5007             for(var i = 0, n; n = ns[i]; i++){
5008                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5009                 if(n){
5010                     result[++ri] = n;
5011                 }
5012             }
5013         }
5014         return result;
5015     };
5016
5017     function concat(a, b){
5018         if(b.slice){
5019             return a.concat(b);
5020         }
5021         for(var i = 0, l = b.length; i < l; i++){
5022             a[a.length] = b[i];
5023         }
5024         return a;
5025     }
5026
5027     function byTag(cs, tagName){
5028         if(cs.tagName || cs == document){
5029             cs = [cs];
5030         }
5031         if(!tagName){
5032             return cs;
5033         }
5034         var r = [], ri = -1;
5035         tagName = tagName.toLowerCase();
5036         for(var i = 0, ci; ci = cs[i]; i++){
5037             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5038                 r[++ri] = ci;
5039             }
5040         }
5041         return r;
5042     };
5043
5044     function byId(cs, attr, id){
5045         if(cs.tagName || cs == document){
5046             cs = [cs];
5047         }
5048         if(!id){
5049             return cs;
5050         }
5051         var r = [], ri = -1;
5052         for(var i = 0,ci; ci = cs[i]; i++){
5053             if(ci && ci.id == id){
5054                 r[++ri] = ci;
5055                 return r;
5056             }
5057         }
5058         return r;
5059     };
5060
5061     function byAttribute(cs, attr, value, op, custom){
5062         var r = [], ri = -1, st = custom=="{";
5063         var f = Roo.DomQuery.operators[op];
5064         for(var i = 0, ci; ci = cs[i]; i++){
5065             var a;
5066             if(st){
5067                 a = Roo.DomQuery.getStyle(ci, attr);
5068             }
5069             else if(attr == "class" || attr == "className"){
5070                 a = ci.className;
5071             }else if(attr == "for"){
5072                 a = ci.htmlFor;
5073             }else if(attr == "href"){
5074                 a = ci.getAttribute("href", 2);
5075             }else{
5076                 a = ci.getAttribute(attr);
5077             }
5078             if((f && f(a, value)) || (!f && a)){
5079                 r[++ri] = ci;
5080             }
5081         }
5082         return r;
5083     };
5084
5085     function byPseudo(cs, name, value){
5086         return Roo.DomQuery.pseudos[name](cs, value);
5087     };
5088
5089     // This is for IE MSXML which does not support expandos.
5090     // IE runs the same speed using setAttribute, however FF slows way down
5091     // and Safari completely fails so they need to continue to use expandos.
5092     var isIE = window.ActiveXObject ? true : false;
5093
5094     // this eval is stop the compressor from
5095     // renaming the variable to something shorter
5096     
5097     /** eval:var:batch */
5098     var batch = 30803; 
5099
5100     var key = 30803;
5101
5102     function nodupIEXml(cs){
5103         var d = ++key;
5104         cs[0].setAttribute("_nodup", d);
5105         var r = [cs[0]];
5106         for(var i = 1, len = cs.length; i < len; i++){
5107             var c = cs[i];
5108             if(!c.getAttribute("_nodup") != d){
5109                 c.setAttribute("_nodup", d);
5110                 r[r.length] = c;
5111             }
5112         }
5113         for(var i = 0, len = cs.length; i < len; i++){
5114             cs[i].removeAttribute("_nodup");
5115         }
5116         return r;
5117     }
5118
5119     function nodup(cs){
5120         if(!cs){
5121             return [];
5122         }
5123         var len = cs.length, c, i, r = cs, cj, ri = -1;
5124         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5125             return cs;
5126         }
5127         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5128             return nodupIEXml(cs);
5129         }
5130         var d = ++key;
5131         cs[0]._nodup = d;
5132         for(i = 1; c = cs[i]; i++){
5133             if(c._nodup != d){
5134                 c._nodup = d;
5135             }else{
5136                 r = [];
5137                 for(var j = 0; j < i; j++){
5138                     r[++ri] = cs[j];
5139                 }
5140                 for(j = i+1; cj = cs[j]; j++){
5141                     if(cj._nodup != d){
5142                         cj._nodup = d;
5143                         r[++ri] = cj;
5144                     }
5145                 }
5146                 return r;
5147             }
5148         }
5149         return r;
5150     }
5151
5152     function quickDiffIEXml(c1, c2){
5153         var d = ++key;
5154         for(var i = 0, len = c1.length; i < len; i++){
5155             c1[i].setAttribute("_qdiff", d);
5156         }
5157         var r = [];
5158         for(var i = 0, len = c2.length; i < len; i++){
5159             if(c2[i].getAttribute("_qdiff") != d){
5160                 r[r.length] = c2[i];
5161             }
5162         }
5163         for(var i = 0, len = c1.length; i < len; i++){
5164            c1[i].removeAttribute("_qdiff");
5165         }
5166         return r;
5167     }
5168
5169     function quickDiff(c1, c2){
5170         var len1 = c1.length;
5171         if(!len1){
5172             return c2;
5173         }
5174         if(isIE && c1[0].selectSingleNode){
5175             return quickDiffIEXml(c1, c2);
5176         }
5177         var d = ++key;
5178         for(var i = 0; i < len1; i++){
5179             c1[i]._qdiff = d;
5180         }
5181         var r = [];
5182         for(var i = 0, len = c2.length; i < len; i++){
5183             if(c2[i]._qdiff != d){
5184                 r[r.length] = c2[i];
5185             }
5186         }
5187         return r;
5188     }
5189
5190     function quickId(ns, mode, root, id){
5191         if(ns == root){
5192            var d = root.ownerDocument || root;
5193            return d.getElementById(id);
5194         }
5195         ns = getNodes(ns, mode, "*");
5196         return byId(ns, null, id);
5197     }
5198
5199     return {
5200         getStyle : function(el, name){
5201             return Roo.fly(el).getStyle(name);
5202         },
5203         /**
5204          * Compiles a selector/xpath query into a reusable function. The returned function
5205          * takes one parameter "root" (optional), which is the context node from where the query should start.
5206          * @param {String} selector The selector/xpath query
5207          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5208          * @return {Function}
5209          */
5210         compile : function(path, type){
5211             type = type || "select";
5212             
5213             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5214             var q = path, mode, lq;
5215             var tk = Roo.DomQuery.matchers;
5216             var tklen = tk.length;
5217             var mm;
5218
5219             // accept leading mode switch
5220             var lmode = q.match(modeRe);
5221             if(lmode && lmode[1]){
5222                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5223                 q = q.replace(lmode[1], "");
5224             }
5225             // strip leading slashes
5226             while(path.substr(0, 1)=="/"){
5227                 path = path.substr(1);
5228             }
5229
5230             while(q && lq != q){
5231                 lq = q;
5232                 var tm = q.match(tagTokenRe);
5233                 if(type == "select"){
5234                     if(tm){
5235                         if(tm[1] == "#"){
5236                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5237                         }else{
5238                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5239                         }
5240                         q = q.replace(tm[0], "");
5241                     }else if(q.substr(0, 1) != '@'){
5242                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5243                     }
5244                 }else{
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }
5253                 }
5254                 while(!(mm = q.match(modeRe))){
5255                     var matched = false;
5256                     for(var j = 0; j < tklen; j++){
5257                         var t = tk[j];
5258                         var m = q.match(t.re);
5259                         if(m){
5260                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5261                                                     return m[i];
5262                                                 });
5263                             q = q.replace(m[0], "");
5264                             matched = true;
5265                             break;
5266                         }
5267                     }
5268                     // prevent infinite loop on bad selector
5269                     if(!matched){
5270                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5271                     }
5272                 }
5273                 if(mm[1]){
5274                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5275                     q = q.replace(mm[1], "");
5276                 }
5277             }
5278             fn[fn.length] = "return nodup(n);\n}";
5279             
5280              /** 
5281               * list of variables that need from compression as they are used by eval.
5282              *  eval:var:batch 
5283              *  eval:var:nodup
5284              *  eval:var:byTag
5285              *  eval:var:ById
5286              *  eval:var:getNodes
5287              *  eval:var:quickId
5288              *  eval:var:mode
5289              *  eval:var:root
5290              *  eval:var:n
5291              *  eval:var:byClassName
5292              *  eval:var:byPseudo
5293              *  eval:var:byAttribute
5294              *  eval:var:attrValue
5295              * 
5296              **/ 
5297             eval(fn.join(""));
5298             return f;
5299         },
5300
5301         /**
5302          * Selects a group of elements.
5303          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5304          * @param {Node} root (optional) The start of the query (defaults to document).
5305          * @return {Array}
5306          */
5307         select : function(path, root, type){
5308             if(!root || root == document){
5309                 root = document;
5310             }
5311             if(typeof root == "string"){
5312                 root = document.getElementById(root);
5313             }
5314             var paths = path.split(",");
5315             var results = [];
5316             for(var i = 0, len = paths.length; i < len; i++){
5317                 var p = paths[i].replace(trimRe, "");
5318                 if(!cache[p]){
5319                     cache[p] = Roo.DomQuery.compile(p);
5320                     if(!cache[p]){
5321                         throw p + " is not a valid selector";
5322                     }
5323                 }
5324                 var result = cache[p](root);
5325                 if(result && result != document){
5326                     results = results.concat(result);
5327                 }
5328             }
5329             if(paths.length > 1){
5330                 return nodup(results);
5331             }
5332             return results;
5333         },
5334
5335         /**
5336          * Selects a single element.
5337          * @param {String} selector The selector/xpath query
5338          * @param {Node} root (optional) The start of the query (defaults to document).
5339          * @return {Element}
5340          */
5341         selectNode : function(path, root){
5342             return Roo.DomQuery.select(path, root)[0];
5343         },
5344
5345         /**
5346          * Selects the value of a node, optionally replacing null with the defaultValue.
5347          * @param {String} selector The selector/xpath query
5348          * @param {Node} root (optional) The start of the query (defaults to document).
5349          * @param {String} defaultValue
5350          */
5351         selectValue : function(path, root, defaultValue){
5352             path = path.replace(trimRe, "");
5353             if(!valueCache[path]){
5354                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5355             }
5356             var n = valueCache[path](root);
5357             n = n[0] ? n[0] : n;
5358             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5359             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5360         },
5361
5362         /**
5363          * Selects the value of a node, parsing integers and floats.
5364          * @param {String} selector The selector/xpath query
5365          * @param {Node} root (optional) The start of the query (defaults to document).
5366          * @param {Number} defaultValue
5367          * @return {Number}
5368          */
5369         selectNumber : function(path, root, defaultValue){
5370             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5371             return parseFloat(v);
5372         },
5373
5374         /**
5375          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5376          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5377          * @param {String} selector The simple selector to test
5378          * @return {Boolean}
5379          */
5380         is : function(el, ss){
5381             if(typeof el == "string"){
5382                 el = document.getElementById(el);
5383             }
5384             var isArray = (el instanceof Array);
5385             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5386             return isArray ? (result.length == el.length) : (result.length > 0);
5387         },
5388
5389         /**
5390          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5391          * @param {Array} el An array of elements to filter
5392          * @param {String} selector The simple selector to test
5393          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5394          * the selector instead of the ones that match
5395          * @return {Array}
5396          */
5397         filter : function(els, ss, nonMatches){
5398             ss = ss.replace(trimRe, "");
5399             if(!simpleCache[ss]){
5400                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5401             }
5402             var result = simpleCache[ss](els);
5403             return nonMatches ? quickDiff(result, els) : result;
5404         },
5405
5406         /**
5407          * Collection of matching regular expressions and code snippets.
5408          */
5409         matchers : [{
5410                 re: /^\.([\w-]+)/,
5411                 select: 'n = byClassName(n, null, " {1} ");'
5412             }, {
5413                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5414                 select: 'n = byPseudo(n, "{1}", "{2}");'
5415             },{
5416                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5417                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5418             }, {
5419                 re: /^#([\w-]+)/,
5420                 select: 'n = byId(n, null, "{1}");'
5421             },{
5422                 re: /^@([\w-]+)/,
5423                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5424             }
5425         ],
5426
5427         /**
5428          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5429          * 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;.
5430          */
5431         operators : {
5432             "=" : function(a, v){
5433                 return a == v;
5434             },
5435             "!=" : function(a, v){
5436                 return a != v;
5437             },
5438             "^=" : function(a, v){
5439                 return a && a.substr(0, v.length) == v;
5440             },
5441             "$=" : function(a, v){
5442                 return a && a.substr(a.length-v.length) == v;
5443             },
5444             "*=" : function(a, v){
5445                 return a && a.indexOf(v) !== -1;
5446             },
5447             "%=" : function(a, v){
5448                 return (a % v) == 0;
5449             },
5450             "|=" : function(a, v){
5451                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5452             },
5453             "~=" : function(a, v){
5454                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5455             }
5456         },
5457
5458         /**
5459          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5460          * and the argument (if any) supplied in the selector.
5461          */
5462         pseudos : {
5463             "first-child" : function(c){
5464                 var r = [], ri = -1, n;
5465                 for(var i = 0, ci; ci = n = c[i]; i++){
5466                     while((n = n.previousSibling) && n.nodeType != 1);
5467                     if(!n){
5468                         r[++ri] = ci;
5469                     }
5470                 }
5471                 return r;
5472             },
5473
5474             "last-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.nextSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "nth-child" : function(c, a) {
5486                 var r = [], ri = -1;
5487                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5488                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5489                 for(var i = 0, n; n = c[i]; i++){
5490                     var pn = n.parentNode;
5491                     if (batch != pn._batch) {
5492                         var j = 0;
5493                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5494                             if(cn.nodeType == 1){
5495                                cn.nodeIndex = ++j;
5496                             }
5497                         }
5498                         pn._batch = batch;
5499                     }
5500                     if (f == 1) {
5501                         if (l == 0 || n.nodeIndex == l){
5502                             r[++ri] = n;
5503                         }
5504                     } else if ((n.nodeIndex + l) % f == 0){
5505                         r[++ri] = n;
5506                     }
5507                 }
5508
5509                 return r;
5510             },
5511
5512             "only-child" : function(c){
5513                 var r = [], ri = -1;;
5514                 for(var i = 0, ci; ci = c[i]; i++){
5515                     if(!prev(ci) && !next(ci)){
5516                         r[++ri] = ci;
5517                     }
5518                 }
5519                 return r;
5520             },
5521
5522             "empty" : function(c){
5523                 var r = [], ri = -1;
5524                 for(var i = 0, ci; ci = c[i]; i++){
5525                     var cns = ci.childNodes, j = 0, cn, empty = true;
5526                     while(cn = cns[j]){
5527                         ++j;
5528                         if(cn.nodeType == 1 || cn.nodeType == 3){
5529                             empty = false;
5530                             break;
5531                         }
5532                     }
5533                     if(empty){
5534                         r[++ri] = ci;
5535                     }
5536                 }
5537                 return r;
5538             },
5539
5540             "contains" : function(c, v){
5541                 var r = [], ri = -1;
5542                 for(var i = 0, ci; ci = c[i]; i++){
5543                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5544                         r[++ri] = ci;
5545                     }
5546                 }
5547                 return r;
5548             },
5549
5550             "nodeValue" : function(c, v){
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5554                         r[++ri] = ci;
5555                     }
5556                 }
5557                 return r;
5558             },
5559
5560             "checked" : function(c){
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     if(ci.checked == true){
5564                         r[++ri] = ci;
5565                     }
5566                 }
5567                 return r;
5568             },
5569
5570             "not" : function(c, ss){
5571                 return Roo.DomQuery.filter(c, ss, true);
5572             },
5573
5574             "odd" : function(c){
5575                 return this["nth-child"](c, "odd");
5576             },
5577
5578             "even" : function(c){
5579                 return this["nth-child"](c, "even");
5580             },
5581
5582             "nth" : function(c, a){
5583                 return c[a-1] || [];
5584             },
5585
5586             "first" : function(c){
5587                 return c[0] || [];
5588             },
5589
5590             "last" : function(c){
5591                 return c[c.length-1] || [];
5592             },
5593
5594             "has" : function(c, ss){
5595                 var s = Roo.DomQuery.select;
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(s(ss, ci).length > 0){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "next" : function(c, ss){
5606                 var is = Roo.DomQuery.is;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     var n = next(ci);
5610                     if(n && is(n, ss)){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "prev" : function(c, ss){
5618                 var is = Roo.DomQuery.is;
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     var n = prev(ci);
5622                     if(n && is(n, ss)){
5623                         r[++ri] = ci;
5624                     }
5625                 }
5626                 return r;
5627             }
5628         }
5629     };
5630 }();
5631
5632 /**
5633  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5634  * @param {String} path The selector/xpath query
5635  * @param {Node} root (optional) The start of the query (defaults to document).
5636  * @return {Array}
5637  * @member Roo
5638  * @method query
5639  */
5640 Roo.query = Roo.DomQuery.select;
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651
5652 /**
5653  * @class Roo.util.Observable
5654  * Base class that provides a common interface for publishing events. Subclasses are expected to
5655  * to have a property "events" with all the events defined.<br>
5656  * For example:
5657  * <pre><code>
5658  Employee = function(name){
5659     this.name = name;
5660     this.addEvents({
5661         "fired" : true,
5662         "quit" : true
5663     });
5664  }
5665  Roo.extend(Employee, Roo.util.Observable);
5666 </code></pre>
5667  * @param {Object} config properties to use (incuding events / listeners)
5668  */
5669
5670 Roo.util.Observable = function(cfg){
5671     
5672     cfg = cfg|| {};
5673     this.addEvents(cfg.events || {});
5674     if (cfg.events) {
5675         delete cfg.events; // make sure
5676     }
5677      
5678     Roo.apply(this, cfg);
5679     
5680     if(this.listeners){
5681         this.on(this.listeners);
5682         delete this.listeners;
5683     }
5684 };
5685 Roo.util.Observable.prototype = {
5686     /** 
5687  * @cfg {Object} listeners  list of events and functions to call for this object, 
5688  * For example :
5689  * <pre><code>
5690     listeners :  { 
5691        'click' : function(e) {
5692            ..... 
5693         } ,
5694         .... 
5695     } 
5696   </code></pre>
5697  */
5698     
5699     
5700     /**
5701      * Fires the specified event with the passed parameters (minus the event name).
5702      * @param {String} eventName
5703      * @param {Object...} args Variable number of parameters are passed to handlers
5704      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5705      */
5706     fireEvent : function(){
5707         var ce = this.events[arguments[0].toLowerCase()];
5708         if(typeof ce == "object"){
5709             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5710         }else{
5711             return true;
5712         }
5713     },
5714
5715     // private
5716     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5717
5718     /**
5719      * Appends an event handler to this component
5720      * @param {String}   eventName The type of event to listen for
5721      * @param {Function} handler The method the event invokes
5722      * @param {Object}   scope (optional) The scope in which to execute the handler
5723      * function. The handler function's "this" context.
5724      * @param {Object}   options (optional) An object containing handler configuration
5725      * properties. This may contain any of the following properties:<ul>
5726      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5727      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5728      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5729      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5730      * by the specified number of milliseconds. If the event fires again within that time, the original
5731      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5732      * </ul><br>
5733      * <p>
5734      * <b>Combining Options</b><br>
5735      * Using the options argument, it is possible to combine different types of listeners:<br>
5736      * <br>
5737      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5738                 <pre><code>
5739                 el.on('click', this.onClick, this, {
5740                         single: true,
5741                 delay: 100,
5742                 forumId: 4
5743                 });
5744                 </code></pre>
5745      * <p>
5746      * <b>Attaching multiple handlers in 1 call</b><br>
5747      * The method also allows for a single argument to be passed which is a config object containing properties
5748      * which specify multiple handlers.
5749      * <pre><code>
5750                 el.on({
5751                         'click': {
5752                         fn: this.onClick,
5753                         scope: this,
5754                         delay: 100
5755                 }, 
5756                 'mouseover': {
5757                         fn: this.onMouseOver,
5758                         scope: this
5759                 },
5760                 'mouseout': {
5761                         fn: this.onMouseOut,
5762                         scope: this
5763                 }
5764                 });
5765                 </code></pre>
5766      * <p>
5767      * Or a shorthand syntax which passes the same scope object to all handlers:
5768         <pre><code>
5769                 el.on({
5770                         'click': this.onClick,
5771                 'mouseover': this.onMouseOver,
5772                 'mouseout': this.onMouseOut,
5773                 scope: this
5774                 });
5775                 </code></pre>
5776      */
5777     addListener : function(eventName, fn, scope, o){
5778         if(typeof eventName == "object"){
5779             o = eventName;
5780             for(var e in o){
5781                 if(this.filterOptRe.test(e)){
5782                     continue;
5783                 }
5784                 if(typeof o[e] == "function"){
5785                     // shared options
5786                     this.addListener(e, o[e], o.scope,  o);
5787                 }else{
5788                     // individual options
5789                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5790                 }
5791             }
5792             return;
5793         }
5794         o = (!o || typeof o == "boolean") ? {} : o;
5795         eventName = eventName.toLowerCase();
5796         var ce = this.events[eventName] || true;
5797         if(typeof ce == "boolean"){
5798             ce = new Roo.util.Event(this, eventName);
5799             this.events[eventName] = ce;
5800         }
5801         ce.addListener(fn, scope, o);
5802     },
5803
5804     /**
5805      * Removes a listener
5806      * @param {String}   eventName     The type of event to listen for
5807      * @param {Function} handler        The handler to remove
5808      * @param {Object}   scope  (optional) The scope (this object) for the handler
5809      */
5810     removeListener : function(eventName, fn, scope){
5811         var ce = this.events[eventName.toLowerCase()];
5812         if(typeof ce == "object"){
5813             ce.removeListener(fn, scope);
5814         }
5815     },
5816
5817     /**
5818      * Removes all listeners for this object
5819      */
5820     purgeListeners : function(){
5821         for(var evt in this.events){
5822             if(typeof this.events[evt] == "object"){
5823                  this.events[evt].clearListeners();
5824             }
5825         }
5826     },
5827
5828     relayEvents : function(o, events){
5829         var createHandler = function(ename){
5830             return function(){
5831                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5832             };
5833         };
5834         for(var i = 0, len = events.length; i < len; i++){
5835             var ename = events[i];
5836             if(!this.events[ename]){ this.events[ename] = true; };
5837             o.on(ename, createHandler(ename), this);
5838         }
5839     },
5840
5841     /**
5842      * Used to define events on this Observable
5843      * @param {Object} object The object with the events defined
5844      */
5845     addEvents : function(o){
5846         if(!this.events){
5847             this.events = {};
5848         }
5849         Roo.applyIf(this.events, o);
5850     },
5851
5852     /**
5853      * Checks to see if this object has any listeners for a specified event
5854      * @param {String} eventName The name of the event to check for
5855      * @return {Boolean} True if the event is being listened for, else false
5856      */
5857     hasListener : function(eventName){
5858         var e = this.events[eventName];
5859         return typeof e == "object" && e.listeners.length > 0;
5860     }
5861 };
5862 /**
5863  * Appends an event handler to this element (shorthand for addListener)
5864  * @param {String}   eventName     The type of event to listen for
5865  * @param {Function} handler        The method the event invokes
5866  * @param {Object}   scope (optional) The scope in which to execute the handler
5867  * function. The handler function's "this" context.
5868  * @param {Object}   options  (optional)
5869  * @method
5870  */
5871 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5872 /**
5873  * Removes a listener (shorthand for removeListener)
5874  * @param {String}   eventName     The type of event to listen for
5875  * @param {Function} handler        The handler to remove
5876  * @param {Object}   scope  (optional) The scope (this object) for the handler
5877  * @method
5878  */
5879 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5880
5881 /**
5882  * Starts capture on the specified Observable. All events will be passed
5883  * to the supplied function with the event name + standard signature of the event
5884  * <b>before</b> the event is fired. If the supplied function returns false,
5885  * the event will not fire.
5886  * @param {Observable} o The Observable to capture
5887  * @param {Function} fn The function to call
5888  * @param {Object} scope (optional) The scope (this object) for the fn
5889  * @static
5890  */
5891 Roo.util.Observable.capture = function(o, fn, scope){
5892     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5893 };
5894
5895 /**
5896  * Removes <b>all</b> added captures from the Observable.
5897  * @param {Observable} o The Observable to release
5898  * @static
5899  */
5900 Roo.util.Observable.releaseCapture = function(o){
5901     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5902 };
5903
5904 (function(){
5905
5906     var createBuffered = function(h, o, scope){
5907         var task = new Roo.util.DelayedTask();
5908         return function(){
5909             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5910         };
5911     };
5912
5913     var createSingle = function(h, e, fn, scope){
5914         return function(){
5915             e.removeListener(fn, scope);
5916             return h.apply(scope, arguments);
5917         };
5918     };
5919
5920     var createDelayed = function(h, o, scope){
5921         return function(){
5922             var args = Array.prototype.slice.call(arguments, 0);
5923             setTimeout(function(){
5924                 h.apply(scope, args);
5925             }, o.delay || 10);
5926         };
5927     };
5928
5929     Roo.util.Event = function(obj, name){
5930         this.name = name;
5931         this.obj = obj;
5932         this.listeners = [];
5933     };
5934
5935     Roo.util.Event.prototype = {
5936         addListener : function(fn, scope, options){
5937             var o = options || {};
5938             scope = scope || this.obj;
5939             if(!this.isListening(fn, scope)){
5940                 var l = {fn: fn, scope: scope, options: o};
5941                 var h = fn;
5942                 if(o.delay){
5943                     h = createDelayed(h, o, scope);
5944                 }
5945                 if(o.single){
5946                     h = createSingle(h, this, fn, scope);
5947                 }
5948                 if(o.buffer){
5949                     h = createBuffered(h, o, scope);
5950                 }
5951                 l.fireFn = h;
5952                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5953                     this.listeners.push(l);
5954                 }else{
5955                     this.listeners = this.listeners.slice(0);
5956                     this.listeners.push(l);
5957                 }
5958             }
5959         },
5960
5961         findListener : function(fn, scope){
5962             scope = scope || this.obj;
5963             var ls = this.listeners;
5964             for(var i = 0, len = ls.length; i < len; i++){
5965                 var l = ls[i];
5966                 if(l.fn == fn && l.scope == scope){
5967                     return i;
5968                 }
5969             }
5970             return -1;
5971         },
5972
5973         isListening : function(fn, scope){
5974             return this.findListener(fn, scope) != -1;
5975         },
5976
5977         removeListener : function(fn, scope){
5978             var index;
5979             if((index = this.findListener(fn, scope)) != -1){
5980                 if(!this.firing){
5981                     this.listeners.splice(index, 1);
5982                 }else{
5983                     this.listeners = this.listeners.slice(0);
5984                     this.listeners.splice(index, 1);
5985                 }
5986                 return true;
5987             }
5988             return false;
5989         },
5990
5991         clearListeners : function(){
5992             this.listeners = [];
5993         },
5994
5995         fire : function(){
5996             var ls = this.listeners, scope, len = ls.length;
5997             if(len > 0){
5998                 this.firing = true;
5999                 var args = Array.prototype.slice.call(arguments, 0);
6000                 for(var i = 0; i < len; i++){
6001                     var l = ls[i];
6002                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6003                         this.firing = false;
6004                         return false;
6005                     }
6006                 }
6007                 this.firing = false;
6008             }
6009             return true;
6010         }
6011     };
6012 })();/*
6013  * Based on:
6014  * Ext JS Library 1.1.1
6015  * Copyright(c) 2006-2007, Ext JS, LLC.
6016  *
6017  * Originally Released Under LGPL - original licence link has changed is not relivant.
6018  *
6019  * Fork - LGPL
6020  * <script type="text/javascript">
6021  */
6022
6023 /**
6024  * @class Roo.EventManager
6025  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6026  * several useful events directly.
6027  * See {@link Roo.EventObject} for more details on normalized event objects.
6028  * @singleton
6029  */
6030 Roo.EventManager = function(){
6031     var docReadyEvent, docReadyProcId, docReadyState = false;
6032     var resizeEvent, resizeTask, textEvent, textSize;
6033     var E = Roo.lib.Event;
6034     var D = Roo.lib.Dom;
6035
6036
6037     var fireDocReady = function(){
6038         if(!docReadyState){
6039             docReadyState = true;
6040             Roo.isReady = true;
6041             if(docReadyProcId){
6042                 clearInterval(docReadyProcId);
6043             }
6044             if(Roo.isGecko || Roo.isOpera) {
6045                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6046             }
6047             if(Roo.isIE){
6048                 var defer = document.getElementById("ie-deferred-loader");
6049                 if(defer){
6050                     defer.onreadystatechange = null;
6051                     defer.parentNode.removeChild(defer);
6052                 }
6053             }
6054             if(docReadyEvent){
6055                 docReadyEvent.fire();
6056                 docReadyEvent.clearListeners();
6057             }
6058         }
6059     };
6060     
6061     var initDocReady = function(){
6062         docReadyEvent = new Roo.util.Event();
6063         if(Roo.isGecko || Roo.isOpera) {
6064             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6065         }else if(Roo.isIE){
6066             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6067             var defer = document.getElementById("ie-deferred-loader");
6068             defer.onreadystatechange = function(){
6069                 if(this.readyState == "complete"){
6070                     fireDocReady();
6071                 }
6072             };
6073         }else if(Roo.isSafari){ 
6074             docReadyProcId = setInterval(function(){
6075                 var rs = document.readyState;
6076                 if(rs == "complete") {
6077                     fireDocReady();     
6078                  }
6079             }, 10);
6080         }
6081         // no matter what, make sure it fires on load
6082         E.on(window, "load", fireDocReady);
6083     };
6084
6085     var createBuffered = function(h, o){
6086         var task = new Roo.util.DelayedTask(h);
6087         return function(e){
6088             // create new event object impl so new events don't wipe out properties
6089             e = new Roo.EventObjectImpl(e);
6090             task.delay(o.buffer, h, null, [e]);
6091         };
6092     };
6093
6094     var createSingle = function(h, el, ename, fn){
6095         return function(e){
6096             Roo.EventManager.removeListener(el, ename, fn);
6097             h(e);
6098         };
6099     };
6100
6101     var createDelayed = function(h, o){
6102         return function(e){
6103             // create new event object impl so new events don't wipe out properties
6104             e = new Roo.EventObjectImpl(e);
6105             setTimeout(function(){
6106                 h(e);
6107             }, o.delay || 10);
6108         };
6109     };
6110
6111     var listen = function(element, ename, opt, fn, scope){
6112         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6113         fn = fn || o.fn; scope = scope || o.scope;
6114         var el = Roo.getDom(element);
6115         if(!el){
6116             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6117         }
6118         var h = function(e){
6119             e = Roo.EventObject.setEvent(e);
6120             var t;
6121             if(o.delegate){
6122                 t = e.getTarget(o.delegate, el);
6123                 if(!t){
6124                     return;
6125                 }
6126             }else{
6127                 t = e.target;
6128             }
6129             if(o.stopEvent === true){
6130                 e.stopEvent();
6131             }
6132             if(o.preventDefault === true){
6133                e.preventDefault();
6134             }
6135             if(o.stopPropagation === true){
6136                 e.stopPropagation();
6137             }
6138
6139             if(o.normalized === false){
6140                 e = e.browserEvent;
6141             }
6142
6143             fn.call(scope || el, e, t, o);
6144         };
6145         if(o.delay){
6146             h = createDelayed(h, o);
6147         }
6148         if(o.single){
6149             h = createSingle(h, el, ename, fn);
6150         }
6151         if(o.buffer){
6152             h = createBuffered(h, o);
6153         }
6154         fn._handlers = fn._handlers || [];
6155         fn._handlers.push([Roo.id(el), ename, h]);
6156
6157         E.on(el, ename, h);
6158         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6159             el.addEventListener("DOMMouseScroll", h, false);
6160             E.on(window, 'unload', function(){
6161                 el.removeEventListener("DOMMouseScroll", h, false);
6162             });
6163         }
6164         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6165             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6166         }
6167         return h;
6168     };
6169
6170     var stopListening = function(el, ename, fn){
6171         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6172         if(hds){
6173             for(var i = 0, len = hds.length; i < len; i++){
6174                 var h = hds[i];
6175                 if(h[0] == id && h[1] == ename){
6176                     hd = h[2];
6177                     hds.splice(i, 1);
6178                     break;
6179                 }
6180             }
6181         }
6182         E.un(el, ename, hd);
6183         el = Roo.getDom(el);
6184         if(ename == "mousewheel" && el.addEventListener){
6185             el.removeEventListener("DOMMouseScroll", hd, false);
6186         }
6187         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6188             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6189         }
6190     };
6191
6192     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6193     
6194     var pub = {
6195         
6196         
6197         /** 
6198          * Fix for doc tools
6199          * @scope Roo.EventManager
6200          */
6201         
6202         
6203         /** 
6204          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6205          * object with a Roo.EventObject
6206          * @param {Function} fn        The method the event invokes
6207          * @param {Object}   scope    An object that becomes the scope of the handler
6208          * @param {boolean}  override If true, the obj passed in becomes
6209          *                             the execution scope of the listener
6210          * @return {Function} The wrapped function
6211          * @deprecated
6212          */
6213         wrap : function(fn, scope, override){
6214             return function(e){
6215                 Roo.EventObject.setEvent(e);
6216                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6217             };
6218         },
6219         
6220         /**
6221      * Appends an event handler to an element (shorthand for addListener)
6222      * @param {String/HTMLElement}   element        The html element or id to assign the
6223      * @param {String}   eventName The type of event to listen for
6224      * @param {Function} handler The method the event invokes
6225      * @param {Object}   scope (optional) The scope in which to execute the handler
6226      * function. The handler function's "this" context.
6227      * @param {Object}   options (optional) An object containing handler configuration
6228      * properties. This may contain any of the following properties:<ul>
6229      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6230      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6231      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6232      * <li>preventDefault {Boolean} True to prevent the default action</li>
6233      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6234      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6235      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6236      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6237      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6238      * by the specified number of milliseconds. If the event fires again within that time, the original
6239      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6240      * </ul><br>
6241      * <p>
6242      * <b>Combining Options</b><br>
6243      * Using the options argument, it is possible to combine different types of listeners:<br>
6244      * <br>
6245      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6246      * Code:<pre><code>
6247 el.on('click', this.onClick, this, {
6248     single: true,
6249     delay: 100,
6250     stopEvent : true,
6251     forumId: 4
6252 });</code></pre>
6253      * <p>
6254      * <b>Attaching multiple handlers in 1 call</b><br>
6255       * The method also allows for a single argument to be passed which is a config object containing properties
6256      * which specify multiple handlers.
6257      * <p>
6258      * Code:<pre><code>
6259 el.on({
6260     'click' : {
6261         fn: this.onClick
6262         scope: this,
6263         delay: 100
6264     },
6265     'mouseover' : {
6266         fn: this.onMouseOver
6267         scope: this
6268     },
6269     'mouseout' : {
6270         fn: this.onMouseOut
6271         scope: this
6272     }
6273 });</code></pre>
6274      * <p>
6275      * Or a shorthand syntax:<br>
6276      * Code:<pre><code>
6277 el.on({
6278     'click' : this.onClick,
6279     'mouseover' : this.onMouseOver,
6280     'mouseout' : this.onMouseOut
6281     scope: this
6282 });</code></pre>
6283      */
6284         addListener : function(element, eventName, fn, scope, options){
6285             if(typeof eventName == "object"){
6286                 var o = eventName;
6287                 for(var e in o){
6288                     if(propRe.test(e)){
6289                         continue;
6290                     }
6291                     if(typeof o[e] == "function"){
6292                         // shared options
6293                         listen(element, e, o, o[e], o.scope);
6294                     }else{
6295                         // individual options
6296                         listen(element, e, o[e]);
6297                     }
6298                 }
6299                 return;
6300             }
6301             return listen(element, eventName, options, fn, scope);
6302         },
6303         
6304         /**
6305          * Removes an event handler
6306          *
6307          * @param {String/HTMLElement}   element        The id or html element to remove the 
6308          *                             event from
6309          * @param {String}   eventName     The type of event
6310          * @param {Function} fn
6311          * @return {Boolean} True if a listener was actually removed
6312          */
6313         removeListener : function(element, eventName, fn){
6314             return stopListening(element, eventName, fn);
6315         },
6316         
6317         /**
6318          * Fires when the document is ready (before onload and before images are loaded). Can be 
6319          * accessed shorthanded Roo.onReady().
6320          * @param {Function} fn        The method the event invokes
6321          * @param {Object}   scope    An  object that becomes the scope of the handler
6322          * @param {boolean}  options
6323          */
6324         onDocumentReady : function(fn, scope, options){
6325             if(docReadyState){ // if it already fired
6326                 docReadyEvent.addListener(fn, scope, options);
6327                 docReadyEvent.fire();
6328                 docReadyEvent.clearListeners();
6329                 return;
6330             }
6331             if(!docReadyEvent){
6332                 initDocReady();
6333             }
6334             docReadyEvent.addListener(fn, scope, options);
6335         },
6336         
6337         /**
6338          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An object that becomes the scope of the handler
6341          * @param {boolean}  options
6342          */
6343         onWindowResize : function(fn, scope, options){
6344             if(!resizeEvent){
6345                 resizeEvent = new Roo.util.Event();
6346                 resizeTask = new Roo.util.DelayedTask(function(){
6347                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6348                 });
6349                 E.on(window, "resize", function(){
6350                     if(Roo.isIE){
6351                         resizeTask.delay(50);
6352                     }else{
6353                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6354                     }
6355                 });
6356             }
6357             resizeEvent.addListener(fn, scope, options);
6358         },
6359
6360         /**
6361          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6362          * @param {Function} fn        The method the event invokes
6363          * @param {Object}   scope    An object that becomes the scope of the handler
6364          * @param {boolean}  options
6365          */
6366         onTextResize : function(fn, scope, options){
6367             if(!textEvent){
6368                 textEvent = new Roo.util.Event();
6369                 var textEl = new Roo.Element(document.createElement('div'));
6370                 textEl.dom.className = 'x-text-resize';
6371                 textEl.dom.innerHTML = 'X';
6372                 textEl.appendTo(document.body);
6373                 textSize = textEl.dom.offsetHeight;
6374                 setInterval(function(){
6375                     if(textEl.dom.offsetHeight != textSize){
6376                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6377                     }
6378                 }, this.textResizeInterval);
6379             }
6380             textEvent.addListener(fn, scope, options);
6381         },
6382
6383         /**
6384          * Removes the passed window resize listener.
6385          * @param {Function} fn        The method the event invokes
6386          * @param {Object}   scope    The scope of handler
6387          */
6388         removeResizeListener : function(fn, scope){
6389             if(resizeEvent){
6390                 resizeEvent.removeListener(fn, scope);
6391             }
6392         },
6393
6394         // private
6395         fireResize : function(){
6396             if(resizeEvent){
6397                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398             }   
6399         },
6400         /**
6401          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6402          */
6403         ieDeferSrc : false,
6404         /**
6405          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6406          */
6407         textResizeInterval : 50
6408     };
6409     
6410     /**
6411      * Fix for doc tools
6412      * @scopeAlias pub=Roo.EventManager
6413      */
6414     
6415      /**
6416      * Appends an event handler to an element (shorthand for addListener)
6417      * @param {String/HTMLElement}   element        The html element or id to assign the
6418      * @param {String}   eventName The type of event to listen for
6419      * @param {Function} handler The method the event invokes
6420      * @param {Object}   scope (optional) The scope in which to execute the handler
6421      * function. The handler function's "this" context.
6422      * @param {Object}   options (optional) An object containing handler configuration
6423      * properties. This may contain any of the following properties:<ul>
6424      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6425      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6426      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6427      * <li>preventDefault {Boolean} True to prevent the default action</li>
6428      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6429      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6430      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6431      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6432      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6433      * by the specified number of milliseconds. If the event fires again within that time, the original
6434      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6435      * </ul><br>
6436      * <p>
6437      * <b>Combining Options</b><br>
6438      * Using the options argument, it is possible to combine different types of listeners:<br>
6439      * <br>
6440      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6441      * Code:<pre><code>
6442 el.on('click', this.onClick, this, {
6443     single: true,
6444     delay: 100,
6445     stopEvent : true,
6446     forumId: 4
6447 });</code></pre>
6448      * <p>
6449      * <b>Attaching multiple handlers in 1 call</b><br>
6450       * The method also allows for a single argument to be passed which is a config object containing properties
6451      * which specify multiple handlers.
6452      * <p>
6453      * Code:<pre><code>
6454 el.on({
6455     'click' : {
6456         fn: this.onClick
6457         scope: this,
6458         delay: 100
6459     },
6460     'mouseover' : {
6461         fn: this.onMouseOver
6462         scope: this
6463     },
6464     'mouseout' : {
6465         fn: this.onMouseOut
6466         scope: this
6467     }
6468 });</code></pre>
6469      * <p>
6470      * Or a shorthand syntax:<br>
6471      * Code:<pre><code>
6472 el.on({
6473     'click' : this.onClick,
6474     'mouseover' : this.onMouseOver,
6475     'mouseout' : this.onMouseOut
6476     scope: this
6477 });</code></pre>
6478      */
6479     pub.on = pub.addListener;
6480     pub.un = pub.removeListener;
6481
6482     pub.stoppedMouseDownEvent = new Roo.util.Event();
6483     return pub;
6484 }();
6485 /**
6486   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6487   * @param {Function} fn        The method the event invokes
6488   * @param {Object}   scope    An  object that becomes the scope of the handler
6489   * @param {boolean}  override If true, the obj passed in becomes
6490   *                             the execution scope of the listener
6491   * @member Roo
6492   * @method onReady
6493  */
6494 Roo.onReady = Roo.EventManager.onDocumentReady;
6495
6496 Roo.onReady(function(){
6497     var bd = Roo.get(document.body);
6498     if(!bd){ return; }
6499
6500     var cls = [
6501             Roo.isIE ? "roo-ie"
6502             : Roo.isGecko ? "roo-gecko"
6503             : Roo.isOpera ? "roo-opera"
6504             : Roo.isSafari ? "roo-safari" : ""];
6505
6506     if(Roo.isMac){
6507         cls.push("roo-mac");
6508     }
6509     if(Roo.isLinux){
6510         cls.push("roo-linux");
6511     }
6512     if(Roo.isBorderBox){
6513         cls.push('roo-border-box');
6514     }
6515     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6516         var p = bd.dom.parentNode;
6517         if(p){
6518             p.className += ' roo-strict';
6519         }
6520     }
6521     bd.addClass(cls.join(' '));
6522 });
6523
6524 /**
6525  * @class Roo.EventObject
6526  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6527  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6528  * Example:
6529  * <pre><code>
6530  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6531     e.preventDefault();
6532     var target = e.getTarget();
6533     ...
6534  }
6535  var myDiv = Roo.get("myDiv");
6536  myDiv.on("click", handleClick);
6537  //or
6538  Roo.EventManager.on("myDiv", 'click', handleClick);
6539  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6540  </code></pre>
6541  * @singleton
6542  */
6543 Roo.EventObject = function(){
6544     
6545     var E = Roo.lib.Event;
6546     
6547     // safari keypress events for special keys return bad keycodes
6548     var safariKeys = {
6549         63234 : 37, // left
6550         63235 : 39, // right
6551         63232 : 38, // up
6552         63233 : 40, // down
6553         63276 : 33, // page up
6554         63277 : 34, // page down
6555         63272 : 46, // delete
6556         63273 : 36, // home
6557         63275 : 35  // end
6558     };
6559
6560     // normalize button clicks
6561     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6562                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6563
6564     Roo.EventObjectImpl = function(e){
6565         if(e){
6566             this.setEvent(e.browserEvent || e);
6567         }
6568     };
6569     Roo.EventObjectImpl.prototype = {
6570         /**
6571          * Used to fix doc tools.
6572          * @scope Roo.EventObject.prototype
6573          */
6574             
6575
6576         
6577         
6578         /** The normal browser event */
6579         browserEvent : null,
6580         /** The button pressed in a mouse event */
6581         button : -1,
6582         /** True if the shift key was down during the event */
6583         shiftKey : false,
6584         /** True if the control key was down during the event */
6585         ctrlKey : false,
6586         /** True if the alt key was down during the event */
6587         altKey : false,
6588
6589         /** Key constant 
6590         * @type Number */
6591         BACKSPACE : 8,
6592         /** Key constant 
6593         * @type Number */
6594         TAB : 9,
6595         /** Key constant 
6596         * @type Number */
6597         RETURN : 13,
6598         /** Key constant 
6599         * @type Number */
6600         ENTER : 13,
6601         /** Key constant 
6602         * @type Number */
6603         SHIFT : 16,
6604         /** Key constant 
6605         * @type Number */
6606         CONTROL : 17,
6607         /** Key constant 
6608         * @type Number */
6609         ESC : 27,
6610         /** Key constant 
6611         * @type Number */
6612         SPACE : 32,
6613         /** Key constant 
6614         * @type Number */
6615         PAGEUP : 33,
6616         /** Key constant 
6617         * @type Number */
6618         PAGEDOWN : 34,
6619         /** Key constant 
6620         * @type Number */
6621         END : 35,
6622         /** Key constant 
6623         * @type Number */
6624         HOME : 36,
6625         /** Key constant 
6626         * @type Number */
6627         LEFT : 37,
6628         /** Key constant 
6629         * @type Number */
6630         UP : 38,
6631         /** Key constant 
6632         * @type Number */
6633         RIGHT : 39,
6634         /** Key constant 
6635         * @type Number */
6636         DOWN : 40,
6637         /** Key constant 
6638         * @type Number */
6639         DELETE : 46,
6640         /** Key constant 
6641         * @type Number */
6642         F5 : 116,
6643
6644            /** @private */
6645         setEvent : function(e){
6646             if(e == this || (e && e.browserEvent)){ // already wrapped
6647                 return e;
6648             }
6649             this.browserEvent = e;
6650             if(e){
6651                 // normalize buttons
6652                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6653                 if(e.type == 'click' && this.button == -1){
6654                     this.button = 0;
6655                 }
6656                 this.type = e.type;
6657                 this.shiftKey = e.shiftKey;
6658                 // mac metaKey behaves like ctrlKey
6659                 this.ctrlKey = e.ctrlKey || e.metaKey;
6660                 this.altKey = e.altKey;
6661                 // in getKey these will be normalized for the mac
6662                 this.keyCode = e.keyCode;
6663                 // keyup warnings on firefox.
6664                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6665                 // cache the target for the delayed and or buffered events
6666                 this.target = E.getTarget(e);
6667                 // same for XY
6668                 this.xy = E.getXY(e);
6669             }else{
6670                 this.button = -1;
6671                 this.shiftKey = false;
6672                 this.ctrlKey = false;
6673                 this.altKey = false;
6674                 this.keyCode = 0;
6675                 this.charCode =0;
6676                 this.target = null;
6677                 this.xy = [0, 0];
6678             }
6679             return this;
6680         },
6681
6682         /**
6683          * Stop the event (preventDefault and stopPropagation)
6684          */
6685         stopEvent : function(){
6686             if(this.browserEvent){
6687                 if(this.browserEvent.type == 'mousedown'){
6688                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6689                 }
6690                 E.stopEvent(this.browserEvent);
6691             }
6692         },
6693
6694         /**
6695          * Prevents the browsers default handling of the event.
6696          */
6697         preventDefault : function(){
6698             if(this.browserEvent){
6699                 E.preventDefault(this.browserEvent);
6700             }
6701         },
6702
6703         /** @private */
6704         isNavKeyPress : function(){
6705             var k = this.keyCode;
6706             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6707             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6708         },
6709
6710         isSpecialKey : function(){
6711             var k = this.keyCode;
6712             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6713             (k == 16) || (k == 17) ||
6714             (k >= 18 && k <= 20) ||
6715             (k >= 33 && k <= 35) ||
6716             (k >= 36 && k <= 39) ||
6717             (k >= 44 && k <= 45);
6718         },
6719         /**
6720          * Cancels bubbling of the event.
6721          */
6722         stopPropagation : function(){
6723             if(this.browserEvent){
6724                 if(this.type == 'mousedown'){
6725                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6726                 }
6727                 E.stopPropagation(this.browserEvent);
6728             }
6729         },
6730
6731         /**
6732          * Gets the key code for the event.
6733          * @return {Number}
6734          */
6735         getCharCode : function(){
6736             return this.charCode || this.keyCode;
6737         },
6738
6739         /**
6740          * Returns a normalized keyCode for the event.
6741          * @return {Number} The key code
6742          */
6743         getKey : function(){
6744             var k = this.keyCode || this.charCode;
6745             return Roo.isSafari ? (safariKeys[k] || k) : k;
6746         },
6747
6748         /**
6749          * Gets the x coordinate of the event.
6750          * @return {Number}
6751          */
6752         getPageX : function(){
6753             return this.xy[0];
6754         },
6755
6756         /**
6757          * Gets the y coordinate of the event.
6758          * @return {Number}
6759          */
6760         getPageY : function(){
6761             return this.xy[1];
6762         },
6763
6764         /**
6765          * Gets the time of the event.
6766          * @return {Number}
6767          */
6768         getTime : function(){
6769             if(this.browserEvent){
6770                 return E.getTime(this.browserEvent);
6771             }
6772             return null;
6773         },
6774
6775         /**
6776          * Gets the page coordinates of the event.
6777          * @return {Array} The xy values like [x, y]
6778          */
6779         getXY : function(){
6780             return this.xy;
6781         },
6782
6783         /**
6784          * Gets the target for the event.
6785          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6786          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6787                 search as a number or element (defaults to 10 || document.body)
6788          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6789          * @return {HTMLelement}
6790          */
6791         getTarget : function(selector, maxDepth, returnEl){
6792             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6793         },
6794         /**
6795          * Gets the related target.
6796          * @return {HTMLElement}
6797          */
6798         getRelatedTarget : function(){
6799             if(this.browserEvent){
6800                 return E.getRelatedTarget(this.browserEvent);
6801             }
6802             return null;
6803         },
6804
6805         /**
6806          * Normalizes mouse wheel delta across browsers
6807          * @return {Number} The delta
6808          */
6809         getWheelDelta : function(){
6810             var e = this.browserEvent;
6811             var delta = 0;
6812             if(e.wheelDelta){ /* IE/Opera. */
6813                 delta = e.wheelDelta/120;
6814             }else if(e.detail){ /* Mozilla case. */
6815                 delta = -e.detail/3;
6816             }
6817             return delta;
6818         },
6819
6820         /**
6821          * Returns true if the control, meta, shift or alt key was pressed during this event.
6822          * @return {Boolean}
6823          */
6824         hasModifier : function(){
6825             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6826         },
6827
6828         /**
6829          * Returns true if the target of this event equals el or is a child of el
6830          * @param {String/HTMLElement/Element} el
6831          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6832          * @return {Boolean}
6833          */
6834         within : function(el, related){
6835             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6836             return t && Roo.fly(el).contains(t);
6837         },
6838
6839         getPoint : function(){
6840             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6841         }
6842     };
6843
6844     return new Roo.EventObjectImpl();
6845 }();
6846             
6847     /*
6848  * Based on:
6849  * Ext JS Library 1.1.1
6850  * Copyright(c) 2006-2007, Ext JS, LLC.
6851  *
6852  * Originally Released Under LGPL - original licence link has changed is not relivant.
6853  *
6854  * Fork - LGPL
6855  * <script type="text/javascript">
6856  */
6857
6858  
6859 // was in Composite Element!??!?!
6860  
6861 (function(){
6862     var D = Roo.lib.Dom;
6863     var E = Roo.lib.Event;
6864     var A = Roo.lib.Anim;
6865
6866     // local style camelizing for speed
6867     var propCache = {};
6868     var camelRe = /(-[a-z])/gi;
6869     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6870     var view = document.defaultView;
6871
6872 /**
6873  * @class Roo.Element
6874  * Represents an Element in the DOM.<br><br>
6875  * Usage:<br>
6876 <pre><code>
6877 var el = Roo.get("my-div");
6878
6879 // or with getEl
6880 var el = getEl("my-div");
6881
6882 // or with a DOM element
6883 var el = Roo.get(myDivElement);
6884 </code></pre>
6885  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6886  * each call instead of constructing a new one.<br><br>
6887  * <b>Animations</b><br />
6888  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6889  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6890 <pre>
6891 Option    Default   Description
6892 --------- --------  ---------------------------------------------
6893 duration  .35       The duration of the animation in seconds
6894 easing    easeOut   The YUI easing method
6895 callback  none      A function to execute when the anim completes
6896 scope     this      The scope (this) of the callback function
6897 </pre>
6898 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6899 * manipulate the animation. Here's an example:
6900 <pre><code>
6901 var el = Roo.get("my-div");
6902
6903 // no animation
6904 el.setWidth(100);
6905
6906 // default animation
6907 el.setWidth(100, true);
6908
6909 // animation with some options set
6910 el.setWidth(100, {
6911     duration: 1,
6912     callback: this.foo,
6913     scope: this
6914 });
6915
6916 // using the "anim" property to get the Anim object
6917 var opt = {
6918     duration: 1,
6919     callback: this.foo,
6920     scope: this
6921 };
6922 el.setWidth(100, opt);
6923 ...
6924 if(opt.anim.isAnimated()){
6925     opt.anim.stop();
6926 }
6927 </code></pre>
6928 * <b> Composite (Collections of) Elements</b><br />
6929  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6930  * @constructor Create a new Element directly.
6931  * @param {String/HTMLElement} element
6932  * @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).
6933  */
6934     Roo.Element = function(element, forceNew){
6935         var dom = typeof element == "string" ?
6936                 document.getElementById(element) : element;
6937         if(!dom){ // invalid id/element
6938             return null;
6939         }
6940         var id = dom.id;
6941         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6942             return Roo.Element.cache[id];
6943         }
6944
6945         /**
6946          * The DOM element
6947          * @type HTMLElement
6948          */
6949         this.dom = dom;
6950
6951         /**
6952          * The DOM element ID
6953          * @type String
6954          */
6955         this.id = id || Roo.id(dom);
6956     };
6957
6958     var El = Roo.Element;
6959
6960     El.prototype = {
6961         /**
6962          * The element's default display mode  (defaults to "")
6963          * @type String
6964          */
6965         originalDisplay : "",
6966
6967         visibilityMode : 1,
6968         /**
6969          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6970          * @type String
6971          */
6972         defaultUnit : "px",
6973         /**
6974          * Sets the element's visibility mode. When setVisible() is called it
6975          * will use this to determine whether to set the visibility or the display property.
6976          * @param visMode Element.VISIBILITY or Element.DISPLAY
6977          * @return {Roo.Element} this
6978          */
6979         setVisibilityMode : function(visMode){
6980             this.visibilityMode = visMode;
6981             return this;
6982         },
6983         /**
6984          * Convenience method for setVisibilityMode(Element.DISPLAY)
6985          * @param {String} display (optional) What to set display to when visible
6986          * @return {Roo.Element} this
6987          */
6988         enableDisplayMode : function(display){
6989             this.setVisibilityMode(El.DISPLAY);
6990             if(typeof display != "undefined") this.originalDisplay = display;
6991             return this;
6992         },
6993
6994         /**
6995          * 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)
6996          * @param {String} selector The simple selector to test
6997          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6998                 search as a number or element (defaults to 10 || document.body)
6999          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7000          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7001          */
7002         findParent : function(simpleSelector, maxDepth, returnEl){
7003             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7004             maxDepth = maxDepth || 50;
7005             if(typeof maxDepth != "number"){
7006                 stopEl = Roo.getDom(maxDepth);
7007                 maxDepth = 10;
7008             }
7009             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7010                 if(dq.is(p, simpleSelector)){
7011                     return returnEl ? Roo.get(p) : p;
7012                 }
7013                 depth++;
7014                 p = p.parentNode;
7015             }
7016             return null;
7017         },
7018
7019
7020         /**
7021          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7022          * @param {String} selector The simple selector to test
7023          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7024                 search as a number or element (defaults to 10 || document.body)
7025          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7026          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7027          */
7028         findParentNode : function(simpleSelector, maxDepth, returnEl){
7029             var p = Roo.fly(this.dom.parentNode, '_internal');
7030             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7031         },
7032
7033         /**
7034          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7035          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7036          * @param {String} selector The simple selector to test
7037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7038                 search as a number or element (defaults to 10 || document.body)
7039          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7040          */
7041         up : function(simpleSelector, maxDepth){
7042             return this.findParentNode(simpleSelector, maxDepth, true);
7043         },
7044
7045
7046
7047         /**
7048          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7049          * @param {String} selector The simple selector to test
7050          * @return {Boolean} True if this element matches the selector, else false
7051          */
7052         is : function(simpleSelector){
7053             return Roo.DomQuery.is(this.dom, simpleSelector);
7054         },
7055
7056         /**
7057          * Perform animation on this element.
7058          * @param {Object} args The YUI animation control args
7059          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7060          * @param {Function} onComplete (optional) Function to call when animation completes
7061          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7062          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7063          * @return {Roo.Element} this
7064          */
7065         animate : function(args, duration, onComplete, easing, animType){
7066             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7067             return this;
7068         },
7069
7070         /*
7071          * @private Internal animation call
7072          */
7073         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7074             animType = animType || 'run';
7075             opt = opt || {};
7076             var anim = Roo.lib.Anim[animType](
7077                 this.dom, args,
7078                 (opt.duration || defaultDur) || .35,
7079                 (opt.easing || defaultEase) || 'easeOut',
7080                 function(){
7081                     Roo.callback(cb, this);
7082                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7083                 },
7084                 this
7085             );
7086             opt.anim = anim;
7087             return anim;
7088         },
7089
7090         // private legacy anim prep
7091         preanim : function(a, i){
7092             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7093         },
7094
7095         /**
7096          * Removes worthless text nodes
7097          * @param {Boolean} forceReclean (optional) By default the element
7098          * keeps track if it has been cleaned already so
7099          * you can call this over and over. However, if you update the element and
7100          * need to force a reclean, you can pass true.
7101          */
7102         clean : function(forceReclean){
7103             if(this.isCleaned && forceReclean !== true){
7104                 return this;
7105             }
7106             var ns = /\S/;
7107             var d = this.dom, n = d.firstChild, ni = -1;
7108             while(n){
7109                 var nx = n.nextSibling;
7110                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7111                     d.removeChild(n);
7112                 }else{
7113                     n.nodeIndex = ++ni;
7114                 }
7115                 n = nx;
7116             }
7117             this.isCleaned = true;
7118             return this;
7119         },
7120
7121         // private
7122         calcOffsetsTo : function(el){
7123             el = Roo.get(el);
7124             var d = el.dom;
7125             var restorePos = false;
7126             if(el.getStyle('position') == 'static'){
7127                 el.position('relative');
7128                 restorePos = true;
7129             }
7130             var x = 0, y =0;
7131             var op = this.dom;
7132             while(op && op != d && op.tagName != 'HTML'){
7133                 x+= op.offsetLeft;
7134                 y+= op.offsetTop;
7135                 op = op.offsetParent;
7136             }
7137             if(restorePos){
7138                 el.position('static');
7139             }
7140             return [x, y];
7141         },
7142
7143         /**
7144          * Scrolls this element into view within the passed container.
7145          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7146          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7147          * @return {Roo.Element} this
7148          */
7149         scrollIntoView : function(container, hscroll){
7150             var c = Roo.getDom(container) || document.body;
7151             var el = this.dom;
7152
7153             var o = this.calcOffsetsTo(c),
7154                 l = o[0],
7155                 t = o[1],
7156                 b = t+el.offsetHeight,
7157                 r = l+el.offsetWidth;
7158
7159             var ch = c.clientHeight;
7160             var ct = parseInt(c.scrollTop, 10);
7161             var cl = parseInt(c.scrollLeft, 10);
7162             var cb = ct + ch;
7163             var cr = cl + c.clientWidth;
7164
7165             if(t < ct){
7166                 c.scrollTop = t;
7167             }else if(b > cb){
7168                 c.scrollTop = b-ch;
7169             }
7170
7171             if(hscroll !== false){
7172                 if(l < cl){
7173                     c.scrollLeft = l;
7174                 }else if(r > cr){
7175                     c.scrollLeft = r-c.clientWidth;
7176                 }
7177             }
7178             return this;
7179         },
7180
7181         // private
7182         scrollChildIntoView : function(child, hscroll){
7183             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7184         },
7185
7186         /**
7187          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7188          * the new height may not be available immediately.
7189          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7190          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7191          * @param {Function} onComplete (optional) Function to call when animation completes
7192          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7193          * @return {Roo.Element} this
7194          */
7195         autoHeight : function(animate, duration, onComplete, easing){
7196             var oldHeight = this.getHeight();
7197             this.clip();
7198             this.setHeight(1); // force clipping
7199             setTimeout(function(){
7200                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7201                 if(!animate){
7202                     this.setHeight(height);
7203                     this.unclip();
7204                     if(typeof onComplete == "function"){
7205                         onComplete();
7206                     }
7207                 }else{
7208                     this.setHeight(oldHeight); // restore original height
7209                     this.setHeight(height, animate, duration, function(){
7210                         this.unclip();
7211                         if(typeof onComplete == "function") onComplete();
7212                     }.createDelegate(this), easing);
7213                 }
7214             }.createDelegate(this), 0);
7215             return this;
7216         },
7217
7218         /**
7219          * Returns true if this element is an ancestor of the passed element
7220          * @param {HTMLElement/String} el The element to check
7221          * @return {Boolean} True if this element is an ancestor of el, else false
7222          */
7223         contains : function(el){
7224             if(!el){return false;}
7225             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7226         },
7227
7228         /**
7229          * Checks whether the element is currently visible using both visibility and display properties.
7230          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7231          * @return {Boolean} True if the element is currently visible, else false
7232          */
7233         isVisible : function(deep) {
7234             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7235             if(deep !== true || !vis){
7236                 return vis;
7237             }
7238             var p = this.dom.parentNode;
7239             while(p && p.tagName.toLowerCase() != "body"){
7240                 if(!Roo.fly(p, '_isVisible').isVisible()){
7241                     return false;
7242                 }
7243                 p = p.parentNode;
7244             }
7245             return true;
7246         },
7247
7248         /**
7249          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7250          * @param {String} selector The CSS selector
7251          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7252          * @return {CompositeElement/CompositeElementLite} The composite element
7253          */
7254         select : function(selector, unique){
7255             return El.select(selector, unique, this.dom);
7256         },
7257
7258         /**
7259          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7260          * @param {String} selector The CSS selector
7261          * @return {Array} An array of the matched nodes
7262          */
7263         query : function(selector, unique){
7264             return Roo.DomQuery.select(selector, this.dom);
7265         },
7266
7267         /**
7268          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7271          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7272          */
7273         child : function(selector, returnDom){
7274             var n = Roo.DomQuery.selectNode(selector, this.dom);
7275             return returnDom ? n : Roo.get(n);
7276         },
7277
7278         /**
7279          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         down : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7291          * @param {String} group The group the DD object is member of
7292          * @param {Object} config The DD config object
7293          * @param {Object} overrides An object containing methods to override/implement on the DD object
7294          * @return {Roo.dd.DD} The DD object
7295          */
7296         initDD : function(group, config, overrides){
7297             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7298             return Roo.apply(dd, overrides);
7299         },
7300
7301         /**
7302          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7303          * @param {String} group The group the DDProxy object is member of
7304          * @param {Object} config The DDProxy config object
7305          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7306          * @return {Roo.dd.DDProxy} The DDProxy object
7307          */
7308         initDDProxy : function(group, config, overrides){
7309             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7310             return Roo.apply(dd, overrides);
7311         },
7312
7313         /**
7314          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7315          * @param {String} group The group the DDTarget object is member of
7316          * @param {Object} config The DDTarget config object
7317          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7318          * @return {Roo.dd.DDTarget} The DDTarget object
7319          */
7320         initDDTarget : function(group, config, overrides){
7321             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7322             return Roo.apply(dd, overrides);
7323         },
7324
7325         /**
7326          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7327          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7328          * @param {Boolean} visible Whether the element is visible
7329          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7330          * @return {Roo.Element} this
7331          */
7332          setVisible : function(visible, animate){
7333             if(!animate || !A){
7334                 if(this.visibilityMode == El.DISPLAY){
7335                     this.setDisplayed(visible);
7336                 }else{
7337                     this.fixDisplay();
7338                     this.dom.style.visibility = visible ? "visible" : "hidden";
7339                 }
7340             }else{
7341                 // closure for composites
7342                 var dom = this.dom;
7343                 var visMode = this.visibilityMode;
7344                 if(visible){
7345                     this.setOpacity(.01);
7346                     this.setVisible(true);
7347                 }
7348                 this.anim({opacity: { to: (visible?1:0) }},
7349                       this.preanim(arguments, 1),
7350                       null, .35, 'easeIn', function(){
7351                          if(!visible){
7352                              if(visMode == El.DISPLAY){
7353                                  dom.style.display = "none";
7354                              }else{
7355                                  dom.style.visibility = "hidden";
7356                              }
7357                              Roo.get(dom).setOpacity(1);
7358                          }
7359                      });
7360             }
7361             return this;
7362         },
7363
7364         /**
7365          * Returns true if display is not "none"
7366          * @return {Boolean}
7367          */
7368         isDisplayed : function() {
7369             return this.getStyle("display") != "none";
7370         },
7371
7372         /**
7373          * Toggles the element's visibility or display, depending on visibility mode.
7374          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7375          * @return {Roo.Element} this
7376          */
7377         toggle : function(animate){
7378             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7379             return this;
7380         },
7381
7382         /**
7383          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7384          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7385          * @return {Roo.Element} this
7386          */
7387         setDisplayed : function(value) {
7388             if(typeof value == "boolean"){
7389                value = value ? this.originalDisplay : "none";
7390             }
7391             this.setStyle("display", value);
7392             return this;
7393         },
7394
7395         /**
7396          * Tries to focus the element. Any exceptions are caught and ignored.
7397          * @return {Roo.Element} this
7398          */
7399         focus : function() {
7400             try{
7401                 this.dom.focus();
7402             }catch(e){}
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to blur the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         blur : function() {
7411             try{
7412                 this.dom.blur();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7419          * @param {String/Array} className The CSS class to add, or an array of classes
7420          * @return {Roo.Element} this
7421          */
7422         addClass : function(className){
7423             if(className instanceof Array){
7424                 for(var i = 0, len = className.length; i < len; i++) {
7425                     this.addClass(className[i]);
7426                 }
7427             }else{
7428                 if(className && !this.hasClass(className)){
7429                     this.dom.className = this.dom.className + " " + className;
7430                 }
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7437          * @param {String/Array} className The CSS class to add, or an array of classes
7438          * @return {Roo.Element} this
7439          */
7440         radioClass : function(className){
7441             var siblings = this.dom.parentNode.childNodes;
7442             for(var i = 0; i < siblings.length; i++) {
7443                 var s = siblings[i];
7444                 if(s.nodeType == 1){
7445                     Roo.get(s).removeClass(className);
7446                 }
7447             }
7448             this.addClass(className);
7449             return this;
7450         },
7451
7452         /**
7453          * Removes one or more CSS classes from the element.
7454          * @param {String/Array} className The CSS class to remove, or an array of classes
7455          * @return {Roo.Element} this
7456          */
7457         removeClass : function(className){
7458             if(!className || !this.dom.className){
7459                 return this;
7460             }
7461             if(className instanceof Array){
7462                 for(var i = 0, len = className.length; i < len; i++) {
7463                     this.removeClass(className[i]);
7464                 }
7465             }else{
7466                 if(this.hasClass(className)){
7467                     var re = this.classReCache[className];
7468                     if (!re) {
7469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7470                        this.classReCache[className] = re;
7471                     }
7472                     this.dom.className =
7473                         this.dom.className.replace(re, " ");
7474                 }
7475             }
7476             return this;
7477         },
7478
7479         // private
7480         classReCache: {},
7481
7482         /**
7483          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7484          * @param {String} className The CSS class to toggle
7485          * @return {Roo.Element} this
7486          */
7487         toggleClass : function(className){
7488             if(this.hasClass(className)){
7489                 this.removeClass(className);
7490             }else{
7491                 this.addClass(className);
7492             }
7493             return this;
7494         },
7495
7496         /**
7497          * Checks if the specified CSS class exists on this element's DOM node.
7498          * @param {String} className The CSS class to check for
7499          * @return {Boolean} True if the class exists, else false
7500          */
7501         hasClass : function(className){
7502             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7503         },
7504
7505         /**
7506          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7507          * @param {String} oldClassName The CSS class to replace
7508          * @param {String} newClassName The replacement CSS class
7509          * @return {Roo.Element} this
7510          */
7511         replaceClass : function(oldClassName, newClassName){
7512             this.removeClass(oldClassName);
7513             this.addClass(newClassName);
7514             return this;
7515         },
7516
7517         /**
7518          * Returns an object with properties matching the styles requested.
7519          * For example, el.getStyles('color', 'font-size', 'width') might return
7520          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7521          * @param {String} style1 A style name
7522          * @param {String} style2 A style name
7523          * @param {String} etc.
7524          * @return {Object} The style object
7525          */
7526         getStyles : function(){
7527             var a = arguments, len = a.length, r = {};
7528             for(var i = 0; i < len; i++){
7529                 r[a[i]] = this.getStyle(a[i]);
7530             }
7531             return r;
7532         },
7533
7534         /**
7535          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7536          * @param {String} property The style property whose value is returned.
7537          * @return {String} The current value of the style property for this element.
7538          */
7539         getStyle : function(){
7540             return view && view.getComputedStyle ?
7541                 function(prop){
7542                     var el = this.dom, v, cs, camel;
7543                     if(prop == 'float'){
7544                         prop = "cssFloat";
7545                     }
7546                     if(el.style && (v = el.style[prop])){
7547                         return v;
7548                     }
7549                     if(cs = view.getComputedStyle(el, "")){
7550                         if(!(camel = propCache[prop])){
7551                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7552                         }
7553                         return cs[camel];
7554                     }
7555                     return null;
7556                 } :
7557                 function(prop){
7558                     var el = this.dom, v, cs, camel;
7559                     if(prop == 'opacity'){
7560                         if(typeof el.style.filter == 'string'){
7561                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7562                             if(m){
7563                                 var fv = parseFloat(m[1]);
7564                                 if(!isNaN(fv)){
7565                                     return fv ? fv / 100 : 0;
7566                                 }
7567                             }
7568                         }
7569                         return 1;
7570                     }else if(prop == 'float'){
7571                         prop = "styleFloat";
7572                     }
7573                     if(!(camel = propCache[prop])){
7574                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7575                     }
7576                     if(v = el.style[camel]){
7577                         return v;
7578                     }
7579                     if(cs = el.currentStyle){
7580                         return cs[camel];
7581                     }
7582                     return null;
7583                 };
7584         }(),
7585
7586         /**
7587          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7588          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7589          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7590          * @return {Roo.Element} this
7591          */
7592         setStyle : function(prop, value){
7593             if(typeof prop == "string"){
7594                 
7595                 if (prop == 'float') {
7596                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7597                     return this;
7598                 }
7599                 
7600                 var camel;
7601                 if(!(camel = propCache[prop])){
7602                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                 }
7604                 
7605                 if(camel == 'opacity') {
7606                     this.setOpacity(value);
7607                 }else{
7608                     this.dom.style[camel] = value;
7609                 }
7610             }else{
7611                 for(var style in prop){
7612                     if(typeof prop[style] != "function"){
7613                        this.setStyle(style, prop[style]);
7614                     }
7615                 }
7616             }
7617             return this;
7618         },
7619
7620         /**
7621          * More flexible version of {@link #setStyle} for setting style properties.
7622          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7623          * a function which returns such a specification.
7624          * @return {Roo.Element} this
7625          */
7626         applyStyles : function(style){
7627             Roo.DomHelper.applyStyles(this.dom, style);
7628             return this;
7629         },
7630
7631         /**
7632           * 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).
7633           * @return {Number} The X position of the element
7634           */
7635         getX : function(){
7636             return D.getX(this.dom);
7637         },
7638
7639         /**
7640           * 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).
7641           * @return {Number} The Y position of the element
7642           */
7643         getY : function(){
7644             return D.getY(this.dom);
7645         },
7646
7647         /**
7648           * 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).
7649           * @return {Array} The XY position of the element
7650           */
7651         getXY : function(){
7652             return D.getXY(this.dom);
7653         },
7654
7655         /**
7656          * 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).
7657          * @param {Number} The X position of the element
7658          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7659          * @return {Roo.Element} this
7660          */
7661         setX : function(x, animate){
7662             if(!animate || !A){
7663                 D.setX(this.dom, x);
7664             }else{
7665                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * 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).
7672          * @param {Number} The Y position of the element
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setY : function(y, animate){
7677             if(!animate || !A){
7678                 D.setY(this.dom, y);
7679             }else{
7680                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7687          * @param {String} left The left CSS property value
7688          * @return {Roo.Element} this
7689          */
7690         setLeft : function(left){
7691             this.setStyle("left", this.addUnits(left));
7692             return this;
7693         },
7694
7695         /**
7696          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7697          * @param {String} top The top CSS property value
7698          * @return {Roo.Element} this
7699          */
7700         setTop : function(top){
7701             this.setStyle("top", this.addUnits(top));
7702             return this;
7703         },
7704
7705         /**
7706          * Sets the element's CSS right style.
7707          * @param {String} right The right CSS property value
7708          * @return {Roo.Element} this
7709          */
7710         setRight : function(right){
7711             this.setStyle("right", this.addUnits(right));
7712             return this;
7713         },
7714
7715         /**
7716          * Sets the element's CSS bottom style.
7717          * @param {String} bottom The bottom CSS property value
7718          * @return {Roo.Element} this
7719          */
7720         setBottom : function(bottom){
7721             this.setStyle("bottom", this.addUnits(bottom));
7722             return this;
7723         },
7724
7725         /**
7726          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7727          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7728          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7729          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7730          * @return {Roo.Element} this
7731          */
7732         setXY : function(pos, animate){
7733             if(!animate || !A){
7734                 D.setXY(this.dom, pos);
7735             }else{
7736                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7743          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7744          * @param {Number} x X value for new position (coordinates are page-based)
7745          * @param {Number} y Y value for new position (coordinates are page-based)
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setLocation : function(x, y, animate){
7750             this.setXY([x, y], this.preanim(arguments, 2));
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7756          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7757          * @param {Number} x X value for new position (coordinates are page-based)
7758          * @param {Number} y Y value for new position (coordinates are page-based)
7759          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7760          * @return {Roo.Element} this
7761          */
7762         moveTo : function(x, y, animate){
7763             this.setXY([x, y], this.preanim(arguments, 2));
7764             return this;
7765         },
7766
7767         /**
7768          * Returns the region of the given element.
7769          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7770          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7771          */
7772         getRegion : function(){
7773             return D.getRegion(this.dom);
7774         },
7775
7776         /**
7777          * Returns the offset height of the element
7778          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7779          * @return {Number} The element's height
7780          */
7781         getHeight : function(contentHeight){
7782             var h = this.dom.offsetHeight || 0;
7783             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7784         },
7785
7786         /**
7787          * Returns the offset width of the element
7788          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7789          * @return {Number} The element's width
7790          */
7791         getWidth : function(contentWidth){
7792             var w = this.dom.offsetWidth || 0;
7793             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7794         },
7795
7796         /**
7797          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7798          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7799          * if a height has not been set using CSS.
7800          * @return {Number}
7801          */
7802         getComputedHeight : function(){
7803             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7804             if(!h){
7805                 h = parseInt(this.getStyle('height'), 10) || 0;
7806                 if(!this.isBorderBox()){
7807                     h += this.getFrameWidth('tb');
7808                 }
7809             }
7810             return h;
7811         },
7812
7813         /**
7814          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7815          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7816          * if a width has not been set using CSS.
7817          * @return {Number}
7818          */
7819         getComputedWidth : function(){
7820             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7821             if(!w){
7822                 w = parseInt(this.getStyle('width'), 10) || 0;
7823                 if(!this.isBorderBox()){
7824                     w += this.getFrameWidth('lr');
7825                 }
7826             }
7827             return w;
7828         },
7829
7830         /**
7831          * Returns the size of the element.
7832          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7833          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7834          */
7835         getSize : function(contentSize){
7836             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7837         },
7838
7839         /**
7840          * Returns the width and height of the viewport.
7841          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7842          */
7843         getViewSize : function(){
7844             var d = this.dom, doc = document, aw = 0, ah = 0;
7845             if(d == doc || d == doc.body){
7846                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7847             }else{
7848                 return {
7849                     width : d.clientWidth,
7850                     height: d.clientHeight
7851                 };
7852             }
7853         },
7854
7855         /**
7856          * Returns the value of the "value" attribute
7857          * @param {Boolean} asNumber true to parse the value as a number
7858          * @return {String/Number}
7859          */
7860         getValue : function(asNumber){
7861             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7862         },
7863
7864         // private
7865         adjustWidth : function(width){
7866             if(typeof width == "number"){
7867                 if(this.autoBoxAdjust && !this.isBorderBox()){
7868                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7869                 }
7870                 if(width < 0){
7871                     width = 0;
7872                 }
7873             }
7874             return width;
7875         },
7876
7877         // private
7878         adjustHeight : function(height){
7879             if(typeof height == "number"){
7880                if(this.autoBoxAdjust && !this.isBorderBox()){
7881                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7882                }
7883                if(height < 0){
7884                    height = 0;
7885                }
7886             }
7887             return height;
7888         },
7889
7890         /**
7891          * Set the width of the element
7892          * @param {Number} width The new width
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setWidth : function(width, animate){
7897             width = this.adjustWidth(width);
7898             if(!animate || !A){
7899                 this.dom.style.width = this.addUnits(width);
7900             }else{
7901                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Set the height of the element
7908          * @param {Number} height The new height
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912          setHeight : function(height, animate){
7913             height = this.adjustHeight(height);
7914             if(!animate || !A){
7915                 this.dom.style.height = this.addUnits(height);
7916             }else{
7917                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7918             }
7919             return this;
7920         },
7921
7922         /**
7923          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7924          * @param {Number} width The new width
7925          * @param {Number} height The new height
7926          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7927          * @return {Roo.Element} this
7928          */
7929          setSize : function(width, height, animate){
7930             if(typeof width == "object"){ // in case of object from getSize()
7931                 height = width.height; width = width.width;
7932             }
7933             width = this.adjustWidth(width); height = this.adjustHeight(height);
7934             if(!animate || !A){
7935                 this.dom.style.width = this.addUnits(width);
7936                 this.dom.style.height = this.addUnits(height);
7937             }else{
7938                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7945          * @param {Number} x X value for new position (coordinates are page-based)
7946          * @param {Number} y Y value for new position (coordinates are page-based)
7947          * @param {Number} width The new width
7948          * @param {Number} height The new height
7949          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7950          * @return {Roo.Element} this
7951          */
7952         setBounds : function(x, y, width, height, animate){
7953             if(!animate || !A){
7954                 this.setSize(width, height);
7955                 this.setLocation(x, y);
7956             }else{
7957                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7958                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7959                               this.preanim(arguments, 4), 'motion');
7960             }
7961             return this;
7962         },
7963
7964         /**
7965          * 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.
7966          * @param {Roo.lib.Region} region The region to fill
7967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7968          * @return {Roo.Element} this
7969          */
7970         setRegion : function(region, animate){
7971             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7972             return this;
7973         },
7974
7975         /**
7976          * Appends an event handler
7977          *
7978          * @param {String}   eventName     The type of event to append
7979          * @param {Function} fn        The method the event invokes
7980          * @param {Object} scope       (optional) The scope (this object) of the fn
7981          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7982          */
7983         addListener : function(eventName, fn, scope, options){
7984             if (this.dom) {
7985                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7986             }
7987         },
7988
7989         /**
7990          * Removes an event handler from this element
7991          * @param {String} eventName the type of event to remove
7992          * @param {Function} fn the method the event invokes
7993          * @return {Roo.Element} this
7994          */
7995         removeListener : function(eventName, fn){
7996             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7997             return this;
7998         },
7999
8000         /**
8001          * Removes all previous added listeners from this element
8002          * @return {Roo.Element} this
8003          */
8004         removeAllListeners : function(){
8005             E.purgeElement(this.dom);
8006             return this;
8007         },
8008
8009         relayEvent : function(eventName, observable){
8010             this.on(eventName, function(e){
8011                 observable.fireEvent(eventName, e);
8012             });
8013         },
8014
8015         /**
8016          * Set the opacity of the element
8017          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021          setOpacity : function(opacity, animate){
8022             if(!animate || !A){
8023                 var s = this.dom.style;
8024                 if(Roo.isIE){
8025                     s.zoom = 1;
8026                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8027                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8028                 }else{
8029                     s.opacity = opacity;
8030                 }
8031             }else{
8032                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          * Gets the left X coordinate
8039          * @param {Boolean} local True to get the local css position instead of page coordinate
8040          * @return {Number}
8041          */
8042         getLeft : function(local){
8043             if(!local){
8044                 return this.getX();
8045             }else{
8046                 return parseInt(this.getStyle("left"), 10) || 0;
8047             }
8048         },
8049
8050         /**
8051          * Gets the right X coordinate of the element (element X position + element width)
8052          * @param {Boolean} local True to get the local css position instead of page coordinate
8053          * @return {Number}
8054          */
8055         getRight : function(local){
8056             if(!local){
8057                 return this.getX() + this.getWidth();
8058             }else{
8059                 return (this.getLeft(true) + this.getWidth()) || 0;
8060             }
8061         },
8062
8063         /**
8064          * Gets the top Y coordinate
8065          * @param {Boolean} local True to get the local css position instead of page coordinate
8066          * @return {Number}
8067          */
8068         getTop : function(local) {
8069             if(!local){
8070                 return this.getY();
8071             }else{
8072                 return parseInt(this.getStyle("top"), 10) || 0;
8073             }
8074         },
8075
8076         /**
8077          * Gets the bottom Y coordinate of the element (element Y position + element height)
8078          * @param {Boolean} local True to get the local css position instead of page coordinate
8079          * @return {Number}
8080          */
8081         getBottom : function(local){
8082             if(!local){
8083                 return this.getY() + this.getHeight();
8084             }else{
8085                 return (this.getTop(true) + this.getHeight()) || 0;
8086             }
8087         },
8088
8089         /**
8090         * Initializes positioning on this element. If a desired position is not passed, it will make the
8091         * the element positioned relative IF it is not already positioned.
8092         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8093         * @param {Number} zIndex (optional) The zIndex to apply
8094         * @param {Number} x (optional) Set the page X position
8095         * @param {Number} y (optional) Set the page Y position
8096         */
8097         position : function(pos, zIndex, x, y){
8098             if(!pos){
8099                if(this.getStyle('position') == 'static'){
8100                    this.setStyle('position', 'relative');
8101                }
8102             }else{
8103                 this.setStyle("position", pos);
8104             }
8105             if(zIndex){
8106                 this.setStyle("z-index", zIndex);
8107             }
8108             if(x !== undefined && y !== undefined){
8109                 this.setXY([x, y]);
8110             }else if(x !== undefined){
8111                 this.setX(x);
8112             }else if(y !== undefined){
8113                 this.setY(y);
8114             }
8115         },
8116
8117         /**
8118         * Clear positioning back to the default when the document was loaded
8119         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8120         * @return {Roo.Element} this
8121          */
8122         clearPositioning : function(value){
8123             value = value ||'';
8124             this.setStyle({
8125                 "left": value,
8126                 "right": value,
8127                 "top": value,
8128                 "bottom": value,
8129                 "z-index": "",
8130                 "position" : "static"
8131             });
8132             return this;
8133         },
8134
8135         /**
8136         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8137         * snapshot before performing an update and then restoring the element.
8138         * @return {Object}
8139         */
8140         getPositioning : function(){
8141             var l = this.getStyle("left");
8142             var t = this.getStyle("top");
8143             return {
8144                 "position" : this.getStyle("position"),
8145                 "left" : l,
8146                 "right" : l ? "" : this.getStyle("right"),
8147                 "top" : t,
8148                 "bottom" : t ? "" : this.getStyle("bottom"),
8149                 "z-index" : this.getStyle("z-index")
8150             };
8151         },
8152
8153         /**
8154          * Gets the width of the border(s) for the specified side(s)
8155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8156          * passing lr would get the border (l)eft width + the border (r)ight width.
8157          * @return {Number} The width of the sides passed added together
8158          */
8159         getBorderWidth : function(side){
8160             return this.addStyles(side, El.borders);
8161         },
8162
8163         /**
8164          * Gets the width of the padding(s) for the specified side(s)
8165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8166          * passing lr would get the padding (l)eft + the padding (r)ight.
8167          * @return {Number} The padding of the sides passed added together
8168          */
8169         getPadding : function(side){
8170             return this.addStyles(side, El.paddings);
8171         },
8172
8173         /**
8174         * Set positioning with an object returned by getPositioning().
8175         * @param {Object} posCfg
8176         * @return {Roo.Element} this
8177          */
8178         setPositioning : function(pc){
8179             this.applyStyles(pc);
8180             if(pc.right == "auto"){
8181                 this.dom.style.right = "";
8182             }
8183             if(pc.bottom == "auto"){
8184                 this.dom.style.bottom = "";
8185             }
8186             return this;
8187         },
8188
8189         // private
8190         fixDisplay : function(){
8191             if(this.getStyle("display") == "none"){
8192                 this.setStyle("visibility", "hidden");
8193                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8194                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8195                     this.setStyle("display", "block");
8196                 }
8197             }
8198         },
8199
8200         /**
8201          * Quick set left and top adding default units
8202          * @param {String} left The left CSS property value
8203          * @param {String} top The top CSS property value
8204          * @return {Roo.Element} this
8205          */
8206          setLeftTop : function(left, top){
8207             this.dom.style.left = this.addUnits(left);
8208             this.dom.style.top = this.addUnits(top);
8209             return this;
8210         },
8211
8212         /**
8213          * Move this element relative to its current position.
8214          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8215          * @param {Number} distance How far to move the element in pixels
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219          move : function(direction, distance, animate){
8220             var xy = this.getXY();
8221             direction = direction.toLowerCase();
8222             switch(direction){
8223                 case "l":
8224                 case "left":
8225                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8226                     break;
8227                case "r":
8228                case "right":
8229                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8230                     break;
8231                case "t":
8232                case "top":
8233                case "up":
8234                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8235                     break;
8236                case "b":
8237                case "bottom":
8238                case "down":
8239                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8240                     break;
8241             }
8242             return this;
8243         },
8244
8245         /**
8246          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8247          * @return {Roo.Element} this
8248          */
8249         clip : function(){
8250             if(!this.isClipped){
8251                this.isClipped = true;
8252                this.originalClip = {
8253                    "o": this.getStyle("overflow"),
8254                    "x": this.getStyle("overflow-x"),
8255                    "y": this.getStyle("overflow-y")
8256                };
8257                this.setStyle("overflow", "hidden");
8258                this.setStyle("overflow-x", "hidden");
8259                this.setStyle("overflow-y", "hidden");
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          *  Return clipping (overflow) to original clipping before clip() was called
8266          * @return {Roo.Element} this
8267          */
8268         unclip : function(){
8269             if(this.isClipped){
8270                 this.isClipped = false;
8271                 var o = this.originalClip;
8272                 if(o.o){this.setStyle("overflow", o.o);}
8273                 if(o.x){this.setStyle("overflow-x", o.x);}
8274                 if(o.y){this.setStyle("overflow-y", o.y);}
8275             }
8276             return this;
8277         },
8278
8279
8280         /**
8281          * Gets the x,y coordinates specified by the anchor position on the element.
8282          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8283          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8284          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8285          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8286          * @return {Array} [x, y] An array containing the element's x and y coordinates
8287          */
8288         getAnchorXY : function(anchor, local, s){
8289             //Passing a different size is useful for pre-calculating anchors,
8290             //especially for anchored animations that change the el size.
8291
8292             var w, h, vp = false;
8293             if(!s){
8294                 var d = this.dom;
8295                 if(d == document.body || d == document){
8296                     vp = true;
8297                     w = D.getViewWidth(); h = D.getViewHeight();
8298                 }else{
8299                     w = this.getWidth(); h = this.getHeight();
8300                 }
8301             }else{
8302                 w = s.width;  h = s.height;
8303             }
8304             var x = 0, y = 0, r = Math.round;
8305             switch((anchor || "tl").toLowerCase()){
8306                 case "c":
8307                     x = r(w*.5);
8308                     y = r(h*.5);
8309                 break;
8310                 case "t":
8311                     x = r(w*.5);
8312                     y = 0;
8313                 break;
8314                 case "l":
8315                     x = 0;
8316                     y = r(h*.5);
8317                 break;
8318                 case "r":
8319                     x = w;
8320                     y = r(h*.5);
8321                 break;
8322                 case "b":
8323                     x = r(w*.5);
8324                     y = h;
8325                 break;
8326                 case "tl":
8327                     x = 0;
8328                     y = 0;
8329                 break;
8330                 case "bl":
8331                     x = 0;
8332                     y = h;
8333                 break;
8334                 case "br":
8335                     x = w;
8336                     y = h;
8337                 break;
8338                 case "tr":
8339                     x = w;
8340                     y = 0;
8341                 break;
8342             }
8343             if(local === true){
8344                 return [x, y];
8345             }
8346             if(vp){
8347                 var sc = this.getScroll();
8348                 return [x + sc.left, y + sc.top];
8349             }
8350             //Add the element's offset xy
8351             var o = this.getXY();
8352             return [x+o[0], y+o[1]];
8353         },
8354
8355         /**
8356          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8357          * supported position values.
8358          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8359          * @param {String} position The position to align to.
8360          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8361          * @return {Array} [x, y]
8362          */
8363         getAlignToXY : function(el, p, o){
8364             el = Roo.get(el);
8365             var d = this.dom;
8366             if(!el.dom){
8367                 throw "Element.alignTo with an element that doesn't exist";
8368             }
8369             var c = false; //constrain to viewport
8370             var p1 = "", p2 = "";
8371             o = o || [0,0];
8372
8373             if(!p){
8374                 p = "tl-bl";
8375             }else if(p == "?"){
8376                 p = "tl-bl?";
8377             }else if(p.indexOf("-") == -1){
8378                 p = "tl-" + p;
8379             }
8380             p = p.toLowerCase();
8381             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8382             if(!m){
8383                throw "Element.alignTo with an invalid alignment " + p;
8384             }
8385             p1 = m[1]; p2 = m[2]; c = !!m[3];
8386
8387             //Subtract the aligned el's internal xy from the target's offset xy
8388             //plus custom offset to get the aligned el's new offset xy
8389             var a1 = this.getAnchorXY(p1, true);
8390             var a2 = el.getAnchorXY(p2, false);
8391             var x = a2[0] - a1[0] + o[0];
8392             var y = a2[1] - a1[1] + o[1];
8393             if(c){
8394                 //constrain the aligned el to viewport if necessary
8395                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8396                 // 5px of margin for ie
8397                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8398
8399                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8400                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8401                 //otherwise swap the aligned el to the opposite border of the target.
8402                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8403                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8404                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8405                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8406
8407                var doc = document;
8408                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8409                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8410
8411                if((x+w) > dw + scrollX){
8412                     x = swapX ? r.left-w : dw+scrollX-w;
8413                 }
8414                if(x < scrollX){
8415                    x = swapX ? r.right : scrollX;
8416                }
8417                if((y+h) > dh + scrollY){
8418                     y = swapY ? r.top-h : dh+scrollY-h;
8419                 }
8420                if (y < scrollY){
8421                    y = swapY ? r.bottom : scrollY;
8422                }
8423             }
8424             return [x,y];
8425         },
8426
8427         // private
8428         getConstrainToXY : function(){
8429             var os = {top:0, left:0, bottom:0, right: 0};
8430
8431             return function(el, local, offsets, proposedXY){
8432                 el = Roo.get(el);
8433                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8434
8435                 var vw, vh, vx = 0, vy = 0;
8436                 if(el.dom == document.body || el.dom == document){
8437                     vw = Roo.lib.Dom.getViewWidth();
8438                     vh = Roo.lib.Dom.getViewHeight();
8439                 }else{
8440                     vw = el.dom.clientWidth;
8441                     vh = el.dom.clientHeight;
8442                     if(!local){
8443                         var vxy = el.getXY();
8444                         vx = vxy[0];
8445                         vy = vxy[1];
8446                     }
8447                 }
8448
8449                 var s = el.getScroll();
8450
8451                 vx += offsets.left + s.left;
8452                 vy += offsets.top + s.top;
8453
8454                 vw -= offsets.right;
8455                 vh -= offsets.bottom;
8456
8457                 var vr = vx+vw;
8458                 var vb = vy+vh;
8459
8460                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8461                 var x = xy[0], y = xy[1];
8462                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8463
8464                 // only move it if it needs it
8465                 var moved = false;
8466
8467                 // first validate right/bottom
8468                 if((x + w) > vr){
8469                     x = vr - w;
8470                     moved = true;
8471                 }
8472                 if((y + h) > vb){
8473                     y = vb - h;
8474                     moved = true;
8475                 }
8476                 // then make sure top/left isn't negative
8477                 if(x < vx){
8478                     x = vx;
8479                     moved = true;
8480                 }
8481                 if(y < vy){
8482                     y = vy;
8483                     moved = true;
8484                 }
8485                 return moved ? [x, y] : false;
8486             };
8487         }(),
8488
8489         // private
8490         adjustForConstraints : function(xy, parent, offsets){
8491             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8492         },
8493
8494         /**
8495          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8496          * document it aligns it to the viewport.
8497          * The position parameter is optional, and can be specified in any one of the following formats:
8498          * <ul>
8499          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8500          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8501          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8502          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8503          *   <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
8504          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8505          * </ul>
8506          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8507          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8508          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8509          * that specified in order to enforce the viewport constraints.
8510          * Following are all of the supported anchor positions:
8511     <pre>
8512     Value  Description
8513     -----  -----------------------------
8514     tl     The top left corner (default)
8515     t      The center of the top edge
8516     tr     The top right corner
8517     l      The center of the left edge
8518     c      In the center of the element
8519     r      The center of the right edge
8520     bl     The bottom left corner
8521     b      The center of the bottom edge
8522     br     The bottom right corner
8523     </pre>
8524     Example Usage:
8525     <pre><code>
8526     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8527     el.alignTo("other-el");
8528
8529     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8530     el.alignTo("other-el", "tr?");
8531
8532     // align the bottom right corner of el with the center left edge of other-el
8533     el.alignTo("other-el", "br-l?");
8534
8535     // align the center of el with the bottom left corner of other-el and
8536     // adjust the x position by -6 pixels (and the y position by 0)
8537     el.alignTo("other-el", "c-bl", [-6, 0]);
8538     </code></pre>
8539          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8540          * @param {String} position The position to align to.
8541          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8543          * @return {Roo.Element} this
8544          */
8545         alignTo : function(element, position, offsets, animate){
8546             var xy = this.getAlignToXY(element, position, offsets);
8547             this.setXY(xy, this.preanim(arguments, 3));
8548             return this;
8549         },
8550
8551         /**
8552          * Anchors an element to another element and realigns it when the window is resized.
8553          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8554          * @param {String} position The position to align to.
8555          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8556          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8557          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8558          * is a number, it is used as the buffer delay (defaults to 50ms).
8559          * @param {Function} callback The function to call after the animation finishes
8560          * @return {Roo.Element} this
8561          */
8562         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8563             var action = function(){
8564                 this.alignTo(el, alignment, offsets, animate);
8565                 Roo.callback(callback, this);
8566             };
8567             Roo.EventManager.onWindowResize(action, this);
8568             var tm = typeof monitorScroll;
8569             if(tm != 'undefined'){
8570                 Roo.EventManager.on(window, 'scroll', action, this,
8571                     {buffer: tm == 'number' ? monitorScroll : 50});
8572             }
8573             action.call(this); // align immediately
8574             return this;
8575         },
8576         /**
8577          * Clears any opacity settings from this element. Required in some cases for IE.
8578          * @return {Roo.Element} this
8579          */
8580         clearOpacity : function(){
8581             if (window.ActiveXObject) {
8582                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8583                     this.dom.style.filter = "";
8584                 }
8585             } else {
8586                 this.dom.style.opacity = "";
8587                 this.dom.style["-moz-opacity"] = "";
8588                 this.dom.style["-khtml-opacity"] = "";
8589             }
8590             return this;
8591         },
8592
8593         /**
8594          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8595          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8596          * @return {Roo.Element} this
8597          */
8598         hide : function(animate){
8599             this.setVisible(false, this.preanim(arguments, 0));
8600             return this;
8601         },
8602
8603         /**
8604         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8605         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8606          * @return {Roo.Element} this
8607          */
8608         show : function(animate){
8609             this.setVisible(true, this.preanim(arguments, 0));
8610             return this;
8611         },
8612
8613         /**
8614          * @private Test if size has a unit, otherwise appends the default
8615          */
8616         addUnits : function(size){
8617             return Roo.Element.addUnits(size, this.defaultUnit);
8618         },
8619
8620         /**
8621          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8622          * @return {Roo.Element} this
8623          */
8624         beginMeasure : function(){
8625             var el = this.dom;
8626             if(el.offsetWidth || el.offsetHeight){
8627                 return this; // offsets work already
8628             }
8629             var changed = [];
8630             var p = this.dom, b = document.body; // start with this element
8631             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8632                 var pe = Roo.get(p);
8633                 if(pe.getStyle('display') == 'none'){
8634                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8635                     p.style.visibility = "hidden";
8636                     p.style.display = "block";
8637                 }
8638                 p = p.parentNode;
8639             }
8640             this._measureChanged = changed;
8641             return this;
8642
8643         },
8644
8645         /**
8646          * Restores displays to before beginMeasure was called
8647          * @return {Roo.Element} this
8648          */
8649         endMeasure : function(){
8650             var changed = this._measureChanged;
8651             if(changed){
8652                 for(var i = 0, len = changed.length; i < len; i++) {
8653                     var r = changed[i];
8654                     r.el.style.visibility = r.visibility;
8655                     r.el.style.display = "none";
8656                 }
8657                 this._measureChanged = null;
8658             }
8659             return this;
8660         },
8661
8662         /**
8663         * Update the innerHTML of this element, optionally searching for and processing scripts
8664         * @param {String} html The new HTML
8665         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8666         * @param {Function} callback For async script loading you can be noticed when the update completes
8667         * @return {Roo.Element} this
8668          */
8669         update : function(html, loadScripts, callback){
8670             if(typeof html == "undefined"){
8671                 html = "";
8672             }
8673             if(loadScripts !== true){
8674                 this.dom.innerHTML = html;
8675                 if(typeof callback == "function"){
8676                     callback();
8677                 }
8678                 return this;
8679             }
8680             var id = Roo.id();
8681             var dom = this.dom;
8682
8683             html += '<span id="' + id + '"></span>';
8684
8685             E.onAvailable(id, function(){
8686                 var hd = document.getElementsByTagName("head")[0];
8687                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8688                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8689                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8690
8691                 var match;
8692                 while(match = re.exec(html)){
8693                     var attrs = match[1];
8694                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8695                     if(srcMatch && srcMatch[2]){
8696                        var s = document.createElement("script");
8697                        s.src = srcMatch[2];
8698                        var typeMatch = attrs.match(typeRe);
8699                        if(typeMatch && typeMatch[2]){
8700                            s.type = typeMatch[2];
8701                        }
8702                        hd.appendChild(s);
8703                     }else if(match[2] && match[2].length > 0){
8704                         if(window.execScript) {
8705                            window.execScript(match[2]);
8706                         } else {
8707                             /**
8708                              * eval:var:id
8709                              * eval:var:dom
8710                              * eval:var:html
8711                              * 
8712                              */
8713                            window.eval(match[2]);
8714                         }
8715                     }
8716                 }
8717                 var el = document.getElementById(id);
8718                 if(el){el.parentNode.removeChild(el);}
8719                 if(typeof callback == "function"){
8720                     callback();
8721                 }
8722             });
8723             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8724             return this;
8725         },
8726
8727         /**
8728          * Direct access to the UpdateManager update() method (takes the same parameters).
8729          * @param {String/Function} url The url for this request or a function to call to get the url
8730          * @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}
8731          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8732          * @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.
8733          * @return {Roo.Element} this
8734          */
8735         load : function(){
8736             var um = this.getUpdateManager();
8737             um.update.apply(um, arguments);
8738             return this;
8739         },
8740
8741         /**
8742         * Gets this element's UpdateManager
8743         * @return {Roo.UpdateManager} The UpdateManager
8744         */
8745         getUpdateManager : function(){
8746             if(!this.updateManager){
8747                 this.updateManager = new Roo.UpdateManager(this);
8748             }
8749             return this.updateManager;
8750         },
8751
8752         /**
8753          * Disables text selection for this element (normalized across browsers)
8754          * @return {Roo.Element} this
8755          */
8756         unselectable : function(){
8757             this.dom.unselectable = "on";
8758             this.swallowEvent("selectstart", true);
8759             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8760             this.addClass("x-unselectable");
8761             return this;
8762         },
8763
8764         /**
8765         * Calculates the x, y to center this element on the screen
8766         * @return {Array} The x, y values [x, y]
8767         */
8768         getCenterXY : function(){
8769             return this.getAlignToXY(document, 'c-c');
8770         },
8771
8772         /**
8773         * Centers the Element in either the viewport, or another Element.
8774         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8775         */
8776         center : function(centerIn){
8777             this.alignTo(centerIn || document, 'c-c');
8778             return this;
8779         },
8780
8781         /**
8782          * Tests various css rules/browsers to determine if this element uses a border box
8783          * @return {Boolean}
8784          */
8785         isBorderBox : function(){
8786             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8787         },
8788
8789         /**
8790          * Return a box {x, y, width, height} that can be used to set another elements
8791          * size/location to match this element.
8792          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8793          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8794          * @return {Object} box An object in the format {x, y, width, height}
8795          */
8796         getBox : function(contentBox, local){
8797             var xy;
8798             if(!local){
8799                 xy = this.getXY();
8800             }else{
8801                 var left = parseInt(this.getStyle("left"), 10) || 0;
8802                 var top = parseInt(this.getStyle("top"), 10) || 0;
8803                 xy = [left, top];
8804             }
8805             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8806             if(!contentBox){
8807                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8808             }else{
8809                 var l = this.getBorderWidth("l")+this.getPadding("l");
8810                 var r = this.getBorderWidth("r")+this.getPadding("r");
8811                 var t = this.getBorderWidth("t")+this.getPadding("t");
8812                 var b = this.getBorderWidth("b")+this.getPadding("b");
8813                 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)};
8814             }
8815             bx.right = bx.x + bx.width;
8816             bx.bottom = bx.y + bx.height;
8817             return bx;
8818         },
8819
8820         /**
8821          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8822          for more information about the sides.
8823          * @param {String} sides
8824          * @return {Number}
8825          */
8826         getFrameWidth : function(sides, onlyContentBox){
8827             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8828         },
8829
8830         /**
8831          * 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.
8832          * @param {Object} box The box to fill {x, y, width, height}
8833          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8834          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8835          * @return {Roo.Element} this
8836          */
8837         setBox : function(box, adjust, animate){
8838             var w = box.width, h = box.height;
8839             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8840                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8841                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8842             }
8843             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8844             return this;
8845         },
8846
8847         /**
8848          * Forces the browser to repaint this element
8849          * @return {Roo.Element} this
8850          */
8851          repaint : function(){
8852             var dom = this.dom;
8853             this.addClass("x-repaint");
8854             setTimeout(function(){
8855                 Roo.get(dom).removeClass("x-repaint");
8856             }, 1);
8857             return this;
8858         },
8859
8860         /**
8861          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8862          * then it returns the calculated width of the sides (see getPadding)
8863          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8864          * @return {Object/Number}
8865          */
8866         getMargins : function(side){
8867             if(!side){
8868                 return {
8869                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8870                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8871                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8872                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8873                 };
8874             }else{
8875                 return this.addStyles(side, El.margins);
8876              }
8877         },
8878
8879         // private
8880         addStyles : function(sides, styles){
8881             var val = 0, v, w;
8882             for(var i = 0, len = sides.length; i < len; i++){
8883                 v = this.getStyle(styles[sides.charAt(i)]);
8884                 if(v){
8885                      w = parseInt(v, 10);
8886                      if(w){ val += w; }
8887                 }
8888             }
8889             return val;
8890         },
8891
8892         /**
8893          * Creates a proxy element of this element
8894          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8895          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8896          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8897          * @return {Roo.Element} The new proxy element
8898          */
8899         createProxy : function(config, renderTo, matchBox){
8900             if(renderTo){
8901                 renderTo = Roo.getDom(renderTo);
8902             }else{
8903                 renderTo = document.body;
8904             }
8905             config = typeof config == "object" ?
8906                 config : {tag : "div", cls: config};
8907             var proxy = Roo.DomHelper.append(renderTo, config, true);
8908             if(matchBox){
8909                proxy.setBox(this.getBox());
8910             }
8911             return proxy;
8912         },
8913
8914         /**
8915          * Puts a mask over this element to disable user interaction. Requires core.css.
8916          * This method can only be applied to elements which accept child nodes.
8917          * @param {String} msg (optional) A message to display in the mask
8918          * @param {String} msgCls (optional) A css class to apply to the msg element
8919          * @return {Element} The mask  element
8920          */
8921         mask : function(msg, msgCls)
8922         {
8923             if(this.getStyle("position") == "static"){
8924                 this.setStyle("position", "relative");
8925             }
8926             if(!this._mask){
8927                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8928             }
8929             this.addClass("x-masked");
8930             this._mask.setDisplayed(true);
8931             
8932             // we wander
8933             var z = 0;
8934             var dom = this.dom
8935             while (dom && dom.style) {
8936                 if (!isNaN(parseInt(dom.style.zIndex))) {
8937                     z = Math.max(z, parseInt(dom.style.zIndex));
8938                 }
8939                 dom = dom.parentNode;
8940             }
8941             // if we are masking the body - then it hides everything..
8942             if (this.dom == document.body) {
8943                 z = 1000000;
8944                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8945                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8946             }
8947            
8948             if(typeof msg == 'string'){
8949                 if(!this._maskMsg){
8950                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8951                 }
8952                 var mm = this._maskMsg;
8953                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8954                 mm.dom.firstChild.innerHTML = msg;
8955                 mm.setDisplayed(true);
8956                 mm.center(this);
8957                 mm.setStyle('z-index', z + 102);
8958             }
8959             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8960                 this._mask.setHeight(this.getHeight());
8961             }
8962             this._mask.setStyle('z-index', z + 100);
8963             
8964             return this._mask;
8965         },
8966
8967         /**
8968          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8969          * it is cached for reuse.
8970          */
8971         unmask : function(removeEl){
8972             if(this._mask){
8973                 if(removeEl === true){
8974                     this._mask.remove();
8975                     delete this._mask;
8976                     if(this._maskMsg){
8977                         this._maskMsg.remove();
8978                         delete this._maskMsg;
8979                     }
8980                 }else{
8981                     this._mask.setDisplayed(false);
8982                     if(this._maskMsg){
8983                         this._maskMsg.setDisplayed(false);
8984                     }
8985                 }
8986             }
8987             this.removeClass("x-masked");
8988         },
8989
8990         /**
8991          * Returns true if this element is masked
8992          * @return {Boolean}
8993          */
8994         isMasked : function(){
8995             return this._mask && this._mask.isVisible();
8996         },
8997
8998         /**
8999          * Creates an iframe shim for this element to keep selects and other windowed objects from
9000          * showing through.
9001          * @return {Roo.Element} The new shim element
9002          */
9003         createShim : function(){
9004             var el = document.createElement('iframe');
9005             el.frameBorder = 'no';
9006             el.className = 'roo-shim';
9007             if(Roo.isIE && Roo.isSecure){
9008                 el.src = Roo.SSL_SECURE_URL;
9009             }
9010             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9011             shim.autoBoxAdjust = false;
9012             return shim;
9013         },
9014
9015         /**
9016          * Removes this element from the DOM and deletes it from the cache
9017          */
9018         remove : function(){
9019             if(this.dom.parentNode){
9020                 this.dom.parentNode.removeChild(this.dom);
9021             }
9022             delete El.cache[this.dom.id];
9023         },
9024
9025         /**
9026          * Sets up event handlers to add and remove a css class when the mouse is over this element
9027          * @param {String} className
9028          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9029          * mouseout events for children elements
9030          * @return {Roo.Element} this
9031          */
9032         addClassOnOver : function(className, preventFlicker){
9033             this.on("mouseover", function(){
9034                 Roo.fly(this, '_internal').addClass(className);
9035             }, this.dom);
9036             var removeFn = function(e){
9037                 if(preventFlicker !== true || !e.within(this, true)){
9038                     Roo.fly(this, '_internal').removeClass(className);
9039                 }
9040             };
9041             this.on("mouseout", removeFn, this.dom);
9042             return this;
9043         },
9044
9045         /**
9046          * Sets up event handlers to add and remove a css class when this element has the focus
9047          * @param {String} className
9048          * @return {Roo.Element} this
9049          */
9050         addClassOnFocus : function(className){
9051             this.on("focus", function(){
9052                 Roo.fly(this, '_internal').addClass(className);
9053             }, this.dom);
9054             this.on("blur", function(){
9055                 Roo.fly(this, '_internal').removeClass(className);
9056             }, this.dom);
9057             return this;
9058         },
9059         /**
9060          * 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)
9061          * @param {String} className
9062          * @return {Roo.Element} this
9063          */
9064         addClassOnClick : function(className){
9065             var dom = this.dom;
9066             this.on("mousedown", function(){
9067                 Roo.fly(dom, '_internal').addClass(className);
9068                 var d = Roo.get(document);
9069                 var fn = function(){
9070                     Roo.fly(dom, '_internal').removeClass(className);
9071                     d.removeListener("mouseup", fn);
9072                 };
9073                 d.on("mouseup", fn);
9074             });
9075             return this;
9076         },
9077
9078         /**
9079          * Stops the specified event from bubbling and optionally prevents the default action
9080          * @param {String} eventName
9081          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9082          * @return {Roo.Element} this
9083          */
9084         swallowEvent : function(eventName, preventDefault){
9085             var fn = function(e){
9086                 e.stopPropagation();
9087                 if(preventDefault){
9088                     e.preventDefault();
9089                 }
9090             };
9091             if(eventName instanceof Array){
9092                 for(var i = 0, len = eventName.length; i < len; i++){
9093                      this.on(eventName[i], fn);
9094                 }
9095                 return this;
9096             }
9097             this.on(eventName, fn);
9098             return this;
9099         },
9100
9101         /**
9102          * @private
9103          */
9104       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9105
9106         /**
9107          * Sizes this element to its parent element's dimensions performing
9108          * neccessary box adjustments.
9109          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9110          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9111          * @return {Roo.Element} this
9112          */
9113         fitToParent : function(monitorResize, targetParent) {
9114           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9115           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9116           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9117             return;
9118           }
9119           var p = Roo.get(targetParent || this.dom.parentNode);
9120           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9121           if (monitorResize === true) {
9122             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9123             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9124           }
9125           return this;
9126         },
9127
9128         /**
9129          * Gets the next sibling, skipping text nodes
9130          * @return {HTMLElement} The next sibling or null
9131          */
9132         getNextSibling : function(){
9133             var n = this.dom.nextSibling;
9134             while(n && n.nodeType != 1){
9135                 n = n.nextSibling;
9136             }
9137             return n;
9138         },
9139
9140         /**
9141          * Gets the previous sibling, skipping text nodes
9142          * @return {HTMLElement} The previous sibling or null
9143          */
9144         getPrevSibling : function(){
9145             var n = this.dom.previousSibling;
9146             while(n && n.nodeType != 1){
9147                 n = n.previousSibling;
9148             }
9149             return n;
9150         },
9151
9152
9153         /**
9154          * Appends the passed element(s) to this element
9155          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9156          * @return {Roo.Element} this
9157          */
9158         appendChild: function(el){
9159             el = Roo.get(el);
9160             el.appendTo(this);
9161             return this;
9162         },
9163
9164         /**
9165          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9166          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9167          * automatically generated with the specified attributes.
9168          * @param {HTMLElement} insertBefore (optional) a child element of this element
9169          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9170          * @return {Roo.Element} The new child element
9171          */
9172         createChild: function(config, insertBefore, returnDom){
9173             config = config || {tag:'div'};
9174             if(insertBefore){
9175                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9176             }
9177             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9178         },
9179
9180         /**
9181          * Appends this element to the passed element
9182          * @param {String/HTMLElement/Element} el The new parent element
9183          * @return {Roo.Element} this
9184          */
9185         appendTo: function(el){
9186             el = Roo.getDom(el);
9187             el.appendChild(this.dom);
9188             return this;
9189         },
9190
9191         /**
9192          * Inserts this element before the passed element in the DOM
9193          * @param {String/HTMLElement/Element} el The element to insert before
9194          * @return {Roo.Element} this
9195          */
9196         insertBefore: function(el){
9197             el = Roo.getDom(el);
9198             el.parentNode.insertBefore(this.dom, el);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element after the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert after
9205          * @return {Roo.Element} this
9206          */
9207         insertAfter: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el.nextSibling);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9215          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9216          * @return {Roo.Element} The new child
9217          */
9218         insertFirst: function(el, returnDom){
9219             el = el || {};
9220             if(typeof el == 'object' && !el.nodeType){ // dh config
9221                 return this.createChild(el, this.dom.firstChild, returnDom);
9222             }else{
9223                 el = Roo.getDom(el);
9224                 this.dom.insertBefore(el, this.dom.firstChild);
9225                 return !returnDom ? Roo.get(el) : el;
9226             }
9227         },
9228
9229         /**
9230          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9231          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9232          * @param {String} where (optional) 'before' or 'after' defaults to before
9233          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9234          * @return {Roo.Element} the inserted Element
9235          */
9236         insertSibling: function(el, where, returnDom){
9237             where = where ? where.toLowerCase() : 'before';
9238             el = el || {};
9239             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9240
9241             if(typeof el == 'object' && !el.nodeType){ // dh config
9242                 if(where == 'after' && !this.dom.nextSibling){
9243                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9244                 }else{
9245                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9246                 }
9247
9248             }else{
9249                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9250                             where == 'before' ? this.dom : this.dom.nextSibling);
9251                 if(!returnDom){
9252                     rt = Roo.get(rt);
9253                 }
9254             }
9255             return rt;
9256         },
9257
9258         /**
9259          * Creates and wraps this element with another element
9260          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9261          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9262          * @return {HTMLElement/Element} The newly created wrapper element
9263          */
9264         wrap: function(config, returnDom){
9265             if(!config){
9266                 config = {tag: "div"};
9267             }
9268             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9269             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9270             return newEl;
9271         },
9272
9273         /**
9274          * Replaces the passed element with this element
9275          * @param {String/HTMLElement/Element} el The element to replace
9276          * @return {Roo.Element} this
9277          */
9278         replace: function(el){
9279             el = Roo.get(el);
9280             this.insertBefore(el);
9281             el.remove();
9282             return this;
9283         },
9284
9285         /**
9286          * Inserts an html fragment into this element
9287          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9288          * @param {String} html The HTML fragment
9289          * @param {Boolean} returnEl True to return an Roo.Element
9290          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9291          */
9292         insertHtml : function(where, html, returnEl){
9293             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9294             return returnEl ? Roo.get(el) : el;
9295         },
9296
9297         /**
9298          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9299          * @param {Object} o The object with the attributes
9300          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9301          * @return {Roo.Element} this
9302          */
9303         set : function(o, useSet){
9304             var el = this.dom;
9305             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9306             for(var attr in o){
9307                 if(attr == "style" || typeof o[attr] == "function") continue;
9308                 if(attr=="cls"){
9309                     el.className = o["cls"];
9310                 }else{
9311                     if(useSet) el.setAttribute(attr, o[attr]);
9312                     else el[attr] = o[attr];
9313                 }
9314             }
9315             if(o.style){
9316                 Roo.DomHelper.applyStyles(el, o.style);
9317             }
9318             return this;
9319         },
9320
9321         /**
9322          * Convenience method for constructing a KeyMap
9323          * @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:
9324          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9325          * @param {Function} fn The function to call
9326          * @param {Object} scope (optional) The scope of the function
9327          * @return {Roo.KeyMap} The KeyMap created
9328          */
9329         addKeyListener : function(key, fn, scope){
9330             var config;
9331             if(typeof key != "object" || key instanceof Array){
9332                 config = {
9333                     key: key,
9334                     fn: fn,
9335                     scope: scope
9336                 };
9337             }else{
9338                 config = {
9339                     key : key.key,
9340                     shift : key.shift,
9341                     ctrl : key.ctrl,
9342                     alt : key.alt,
9343                     fn: fn,
9344                     scope: scope
9345                 };
9346             }
9347             return new Roo.KeyMap(this, config);
9348         },
9349
9350         /**
9351          * Creates a KeyMap for this element
9352          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9353          * @return {Roo.KeyMap} The KeyMap created
9354          */
9355         addKeyMap : function(config){
9356             return new Roo.KeyMap(this, config);
9357         },
9358
9359         /**
9360          * Returns true if this element is scrollable.
9361          * @return {Boolean}
9362          */
9363          isScrollable : function(){
9364             var dom = this.dom;
9365             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9366         },
9367
9368         /**
9369          * 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().
9370          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9371          * @param {Number} value The new scroll value
9372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9373          * @return {Element} this
9374          */
9375
9376         scrollTo : function(side, value, animate){
9377             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9378             if(!animate || !A){
9379                 this.dom[prop] = value;
9380             }else{
9381                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9382                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9383             }
9384             return this;
9385         },
9386
9387         /**
9388          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9389          * within this element's scrollable range.
9390          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9391          * @param {Number} distance How far to scroll the element in pixels
9392          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9393          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9394          * was scrolled as far as it could go.
9395          */
9396          scroll : function(direction, distance, animate){
9397              if(!this.isScrollable()){
9398                  return;
9399              }
9400              var el = this.dom;
9401              var l = el.scrollLeft, t = el.scrollTop;
9402              var w = el.scrollWidth, h = el.scrollHeight;
9403              var cw = el.clientWidth, ch = el.clientHeight;
9404              direction = direction.toLowerCase();
9405              var scrolled = false;
9406              var a = this.preanim(arguments, 2);
9407              switch(direction){
9408                  case "l":
9409                  case "left":
9410                      if(w - l > cw){
9411                          var v = Math.min(l + distance, w-cw);
9412                          this.scrollTo("left", v, a);
9413                          scrolled = true;
9414                      }
9415                      break;
9416                 case "r":
9417                 case "right":
9418                      if(l > 0){
9419                          var v = Math.max(l - distance, 0);
9420                          this.scrollTo("left", v, a);
9421                          scrolled = true;
9422                      }
9423                      break;
9424                 case "t":
9425                 case "top":
9426                 case "up":
9427                      if(t > 0){
9428                          var v = Math.max(t - distance, 0);
9429                          this.scrollTo("top", v, a);
9430                          scrolled = true;
9431                      }
9432                      break;
9433                 case "b":
9434                 case "bottom":
9435                 case "down":
9436                      if(h - t > ch){
9437                          var v = Math.min(t + distance, h-ch);
9438                          this.scrollTo("top", v, a);
9439                          scrolled = true;
9440                      }
9441                      break;
9442              }
9443              return scrolled;
9444         },
9445
9446         /**
9447          * Translates the passed page coordinates into left/top css values for this element
9448          * @param {Number/Array} x The page x or an array containing [x, y]
9449          * @param {Number} y The page y
9450          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9451          */
9452         translatePoints : function(x, y){
9453             if(typeof x == 'object' || x instanceof Array){
9454                 y = x[1]; x = x[0];
9455             }
9456             var p = this.getStyle('position');
9457             var o = this.getXY();
9458
9459             var l = parseInt(this.getStyle('left'), 10);
9460             var t = parseInt(this.getStyle('top'), 10);
9461
9462             if(isNaN(l)){
9463                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9464             }
9465             if(isNaN(t)){
9466                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9467             }
9468
9469             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9470         },
9471
9472         /**
9473          * Returns the current scroll position of the element.
9474          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9475          */
9476         getScroll : function(){
9477             var d = this.dom, doc = document;
9478             if(d == doc || d == doc.body){
9479                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9480                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9481                 return {left: l, top: t};
9482             }else{
9483                 return {left: d.scrollLeft, top: d.scrollTop};
9484             }
9485         },
9486
9487         /**
9488          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9489          * are convert to standard 6 digit hex color.
9490          * @param {String} attr The css attribute
9491          * @param {String} defaultValue The default value to use when a valid color isn't found
9492          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9493          * YUI color anims.
9494          */
9495         getColor : function(attr, defaultValue, prefix){
9496             var v = this.getStyle(attr);
9497             if(!v || v == "transparent" || v == "inherit") {
9498                 return defaultValue;
9499             }
9500             var color = typeof prefix == "undefined" ? "#" : prefix;
9501             if(v.substr(0, 4) == "rgb("){
9502                 var rvs = v.slice(4, v.length -1).split(",");
9503                 for(var i = 0; i < 3; i++){
9504                     var h = parseInt(rvs[i]).toString(16);
9505                     if(h < 16){
9506                         h = "0" + h;
9507                     }
9508                     color += h;
9509                 }
9510             } else {
9511                 if(v.substr(0, 1) == "#"){
9512                     if(v.length == 4) {
9513                         for(var i = 1; i < 4; i++){
9514                             var c = v.charAt(i);
9515                             color +=  c + c;
9516                         }
9517                     }else if(v.length == 7){
9518                         color += v.substr(1);
9519                     }
9520                 }
9521             }
9522             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9523         },
9524
9525         /**
9526          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9527          * gradient background, rounded corners and a 4-way shadow.
9528          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9529          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9530          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9531          * @return {Roo.Element} this
9532          */
9533         boxWrap : function(cls){
9534             cls = cls || 'x-box';
9535             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9536             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9537             return el;
9538         },
9539
9540         /**
9541          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9542          * @param {String} namespace The namespace in which to look for the attribute
9543          * @param {String} name The attribute name
9544          * @return {String} The attribute value
9545          */
9546         getAttributeNS : Roo.isIE ? function(ns, name){
9547             var d = this.dom;
9548             var type = typeof d[ns+":"+name];
9549             if(type != 'undefined' && type != 'unknown'){
9550                 return d[ns+":"+name];
9551             }
9552             return d[name];
9553         } : function(ns, name){
9554             var d = this.dom;
9555             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9556         }
9557     };
9558
9559     var ep = El.prototype;
9560
9561     /**
9562      * Appends an event handler (Shorthand for addListener)
9563      * @param {String}   eventName     The type of event to append
9564      * @param {Function} fn        The method the event invokes
9565      * @param {Object} scope       (optional) The scope (this object) of the fn
9566      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9567      * @method
9568      */
9569     ep.on = ep.addListener;
9570         // backwards compat
9571     ep.mon = ep.addListener;
9572
9573     /**
9574      * Removes an event handler from this element (shorthand for removeListener)
9575      * @param {String} eventName the type of event to remove
9576      * @param {Function} fn the method the event invokes
9577      * @return {Roo.Element} this
9578      * @method
9579      */
9580     ep.un = ep.removeListener;
9581
9582     /**
9583      * true to automatically adjust width and height settings for box-model issues (default to true)
9584      */
9585     ep.autoBoxAdjust = true;
9586
9587     // private
9588     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9589
9590     // private
9591     El.addUnits = function(v, defaultUnit){
9592         if(v === "" || v == "auto"){
9593             return v;
9594         }
9595         if(v === undefined){
9596             return '';
9597         }
9598         if(typeof v == "number" || !El.unitPattern.test(v)){
9599             return v + (defaultUnit || 'px');
9600         }
9601         return v;
9602     };
9603
9604     // special markup used throughout Roo when box wrapping elements
9605     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>';
9606     /**
9607      * Visibility mode constant - Use visibility to hide element
9608      * @static
9609      * @type Number
9610      */
9611     El.VISIBILITY = 1;
9612     /**
9613      * Visibility mode constant - Use display to hide element
9614      * @static
9615      * @type Number
9616      */
9617     El.DISPLAY = 2;
9618
9619     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9620     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9621     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9622
9623
9624
9625     /**
9626      * @private
9627      */
9628     El.cache = {};
9629
9630     var docEl;
9631
9632     /**
9633      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9634      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9635      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9636      * @return {Element} The Element object
9637      * @static
9638      */
9639     El.get = function(el){
9640         var ex, elm, id;
9641         if(!el){ return null; }
9642         if(typeof el == "string"){ // element id
9643             if(!(elm = document.getElementById(el))){
9644                 return null;
9645             }
9646             if(ex = El.cache[el]){
9647                 ex.dom = elm;
9648             }else{
9649                 ex = El.cache[el] = new El(elm);
9650             }
9651             return ex;
9652         }else if(el.tagName){ // dom element
9653             if(!(id = el.id)){
9654                 id = Roo.id(el);
9655             }
9656             if(ex = El.cache[id]){
9657                 ex.dom = el;
9658             }else{
9659                 ex = El.cache[id] = new El(el);
9660             }
9661             return ex;
9662         }else if(el instanceof El){
9663             if(el != docEl){
9664                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9665                                                               // catch case where it hasn't been appended
9666                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9667             }
9668             return el;
9669         }else if(el.isComposite){
9670             return el;
9671         }else if(el instanceof Array){
9672             return El.select(el);
9673         }else if(el == document){
9674             // create a bogus element object representing the document object
9675             if(!docEl){
9676                 var f = function(){};
9677                 f.prototype = El.prototype;
9678                 docEl = new f();
9679                 docEl.dom = document;
9680             }
9681             return docEl;
9682         }
9683         return null;
9684     };
9685
9686     // private
9687     El.uncache = function(el){
9688         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9689             if(a[i]){
9690                 delete El.cache[a[i].id || a[i]];
9691             }
9692         }
9693     };
9694
9695     // private
9696     // Garbage collection - uncache elements/purge listeners on orphaned elements
9697     // so we don't hold a reference and cause the browser to retain them
9698     El.garbageCollect = function(){
9699         if(!Roo.enableGarbageCollector){
9700             clearInterval(El.collectorThread);
9701             return;
9702         }
9703         for(var eid in El.cache){
9704             var el = El.cache[eid], d = el.dom;
9705             // -------------------------------------------------------
9706             // Determining what is garbage:
9707             // -------------------------------------------------------
9708             // !d
9709             // dom node is null, definitely garbage
9710             // -------------------------------------------------------
9711             // !d.parentNode
9712             // no parentNode == direct orphan, definitely garbage
9713             // -------------------------------------------------------
9714             // !d.offsetParent && !document.getElementById(eid)
9715             // display none elements have no offsetParent so we will
9716             // also try to look it up by it's id. However, check
9717             // offsetParent first so we don't do unneeded lookups.
9718             // This enables collection of elements that are not orphans
9719             // directly, but somewhere up the line they have an orphan
9720             // parent.
9721             // -------------------------------------------------------
9722             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9723                 delete El.cache[eid];
9724                 if(d && Roo.enableListenerCollection){
9725                     E.purgeElement(d);
9726                 }
9727             }
9728         }
9729     }
9730     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9731
9732
9733     // dom is optional
9734     El.Flyweight = function(dom){
9735         this.dom = dom;
9736     };
9737     El.Flyweight.prototype = El.prototype;
9738
9739     El._flyweights = {};
9740     /**
9741      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9742      * the dom node can be overwritten by other code.
9743      * @param {String/HTMLElement} el The dom node or id
9744      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9745      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9746      * @static
9747      * @return {Element} The shared Element object
9748      */
9749     El.fly = function(el, named){
9750         named = named || '_global';
9751         el = Roo.getDom(el);
9752         if(!el){
9753             return null;
9754         }
9755         if(!El._flyweights[named]){
9756             El._flyweights[named] = new El.Flyweight();
9757         }
9758         El._flyweights[named].dom = el;
9759         return El._flyweights[named];
9760     };
9761
9762     /**
9763      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9764      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9765      * Shorthand of {@link Roo.Element#get}
9766      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9767      * @return {Element} The Element object
9768      * @member Roo
9769      * @method get
9770      */
9771     Roo.get = El.get;
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * Shorthand of {@link Roo.Element#fly}
9776      * @param {String/HTMLElement} el The dom node or id
9777      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9778      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9779      * @static
9780      * @return {Element} The shared Element object
9781      * @member Roo
9782      * @method fly
9783      */
9784     Roo.fly = El.fly;
9785
9786     // speedy lookup for elements never to box adjust
9787     var noBoxAdjust = Roo.isStrict ? {
9788         select:1
9789     } : {
9790         input:1, select:1, textarea:1
9791     };
9792     if(Roo.isIE || Roo.isGecko){
9793         noBoxAdjust['button'] = 1;
9794     }
9795
9796
9797     Roo.EventManager.on(window, 'unload', function(){
9798         delete El.cache;
9799         delete El._flyweights;
9800     });
9801 })();
9802
9803
9804
9805
9806 if(Roo.DomQuery){
9807     Roo.Element.selectorFunction = Roo.DomQuery.select;
9808 }
9809
9810 Roo.Element.select = function(selector, unique, root){
9811     var els;
9812     if(typeof selector == "string"){
9813         els = Roo.Element.selectorFunction(selector, root);
9814     }else if(selector.length !== undefined){
9815         els = selector;
9816     }else{
9817         throw "Invalid selector";
9818     }
9819     if(unique === true){
9820         return new Roo.CompositeElement(els);
9821     }else{
9822         return new Roo.CompositeElementLite(els);
9823     }
9824 };
9825 /**
9826  * Selects elements based on the passed CSS selector to enable working on them as 1.
9827  * @param {String/Array} selector The CSS selector or an array of elements
9828  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9829  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9830  * @return {CompositeElementLite/CompositeElement}
9831  * @member Roo
9832  * @method select
9833  */
9834 Roo.select = Roo.Element.select;
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849 /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861
9862 //Notifies Element that fx methods are available
9863 Roo.enableFx = true;
9864
9865 /**
9866  * @class Roo.Fx
9867  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9868  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9869  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9870  * Element effects to work.</p><br/>
9871  *
9872  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9873  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9874  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9875  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9876  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9877  * expected results and should be done with care.</p><br/>
9878  *
9879  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9880  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9881 <pre>
9882 Value  Description
9883 -----  -----------------------------
9884 tl     The top left corner
9885 t      The center of the top edge
9886 tr     The top right corner
9887 l      The center of the left edge
9888 r      The center of the right edge
9889 bl     The bottom left corner
9890 b      The center of the bottom edge
9891 br     The bottom right corner
9892 </pre>
9893  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9894  * below are common options that can be passed to any Fx method.</b>
9895  * @cfg {Function} callback A function called when the effect is finished
9896  * @cfg {Object} scope The scope of the effect function
9897  * @cfg {String} easing A valid Easing value for the effect
9898  * @cfg {String} afterCls A css class to apply after the effect
9899  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9900  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9901  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9902  * effects that end with the element being visually hidden, ignored otherwise)
9903  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9904  * a function which returns such a specification that will be applied to the Element after the effect finishes
9905  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9906  * @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
9907  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9908  */
9909 Roo.Fx = {
9910         /**
9911          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9912          * origin for the slide effect.  This function automatically handles wrapping the element with
9913          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9914          * Usage:
9915          *<pre><code>
9916 // default: slide the element in from the top
9917 el.slideIn();
9918
9919 // custom: slide the element in from the right with a 2-second duration
9920 el.slideIn('r', { duration: 2 });
9921
9922 // common config options shown with default values
9923 el.slideIn('t', {
9924     easing: 'easeOut',
9925     duration: .5
9926 });
9927 </code></pre>
9928          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9929          * @param {Object} options (optional) Object literal with any of the Fx config options
9930          * @return {Roo.Element} The Element
9931          */
9932     slideIn : function(anchor, o){
9933         var el = this.getFxEl();
9934         o = o || {};
9935
9936         el.queueFx(o, function(){
9937
9938             anchor = anchor || "t";
9939
9940             // fix display to visibility
9941             this.fixDisplay();
9942
9943             // restore values after effect
9944             var r = this.getFxRestore();
9945             var b = this.getBox();
9946             // fixed size for slide
9947             this.setSize(b);
9948
9949             // wrap if needed
9950             var wrap = this.fxWrap(r.pos, o, "hidden");
9951
9952             var st = this.dom.style;
9953             st.visibility = "visible";
9954             st.position = "absolute";
9955
9956             // clear out temp styles after slide and unwrap
9957             var after = function(){
9958                 el.fxUnwrap(wrap, r.pos, o);
9959                 st.width = r.width;
9960                 st.height = r.height;
9961                 el.afterFx(o);
9962             };
9963             // time to calc the positions
9964             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9965
9966             switch(anchor.toLowerCase()){
9967                 case "t":
9968                     wrap.setSize(b.width, 0);
9969                     st.left = st.bottom = "0";
9970                     a = {height: bh};
9971                 break;
9972                 case "l":
9973                     wrap.setSize(0, b.height);
9974                     st.right = st.top = "0";
9975                     a = {width: bw};
9976                 break;
9977                 case "r":
9978                     wrap.setSize(0, b.height);
9979                     wrap.setX(b.right);
9980                     st.left = st.top = "0";
9981                     a = {width: bw, points: pt};
9982                 break;
9983                 case "b":
9984                     wrap.setSize(b.width, 0);
9985                     wrap.setY(b.bottom);
9986                     st.left = st.top = "0";
9987                     a = {height: bh, points: pt};
9988                 break;
9989                 case "tl":
9990                     wrap.setSize(0, 0);
9991                     st.right = st.bottom = "0";
9992                     a = {width: bw, height: bh};
9993                 break;
9994                 case "bl":
9995                     wrap.setSize(0, 0);
9996                     wrap.setY(b.y+b.height);
9997                     st.right = st.top = "0";
9998                     a = {width: bw, height: bh, points: pt};
9999                 break;
10000                 case "br":
10001                     wrap.setSize(0, 0);
10002                     wrap.setXY([b.right, b.bottom]);
10003                     st.left = st.top = "0";
10004                     a = {width: bw, height: bh, points: pt};
10005                 break;
10006                 case "tr":
10007                     wrap.setSize(0, 0);
10008                     wrap.setX(b.x+b.width);
10009                     st.left = st.bottom = "0";
10010                     a = {width: bw, height: bh, points: pt};
10011                 break;
10012             }
10013             this.dom.style.visibility = "visible";
10014             wrap.show();
10015
10016             arguments.callee.anim = wrap.fxanim(a,
10017                 o,
10018                 'motion',
10019                 .5,
10020                 'easeOut', after);
10021         });
10022         return this;
10023     },
10024     
10025         /**
10026          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10027          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10028          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10029          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10030          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10031          * Usage:
10032          *<pre><code>
10033 // default: slide the element out to the top
10034 el.slideOut();
10035
10036 // custom: slide the element out to the right with a 2-second duration
10037 el.slideOut('r', { duration: 2 });
10038
10039 // common config options shown with default values
10040 el.slideOut('t', {
10041     easing: 'easeOut',
10042     duration: .5,
10043     remove: false,
10044     useDisplay: false
10045 });
10046 </code></pre>
10047          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10048          * @param {Object} options (optional) Object literal with any of the Fx config options
10049          * @return {Roo.Element} The Element
10050          */
10051     slideOut : function(anchor, o){
10052         var el = this.getFxEl();
10053         o = o || {};
10054
10055         el.queueFx(o, function(){
10056
10057             anchor = anchor || "t";
10058
10059             // restore values after effect
10060             var r = this.getFxRestore();
10061             
10062             var b = this.getBox();
10063             // fixed size for slide
10064             this.setSize(b);
10065
10066             // wrap if needed
10067             var wrap = this.fxWrap(r.pos, o, "visible");
10068
10069             var st = this.dom.style;
10070             st.visibility = "visible";
10071             st.position = "absolute";
10072
10073             wrap.setSize(b);
10074
10075             var after = function(){
10076                 if(o.useDisplay){
10077                     el.setDisplayed(false);
10078                 }else{
10079                     el.hide();
10080                 }
10081
10082                 el.fxUnwrap(wrap, r.pos, o);
10083
10084                 st.width = r.width;
10085                 st.height = r.height;
10086
10087                 el.afterFx(o);
10088             };
10089
10090             var a, zero = {to: 0};
10091             switch(anchor.toLowerCase()){
10092                 case "t":
10093                     st.left = st.bottom = "0";
10094                     a = {height: zero};
10095                 break;
10096                 case "l":
10097                     st.right = st.top = "0";
10098                     a = {width: zero};
10099                 break;
10100                 case "r":
10101                     st.left = st.top = "0";
10102                     a = {width: zero, points: {to:[b.right, b.y]}};
10103                 break;
10104                 case "b":
10105                     st.left = st.top = "0";
10106                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10107                 break;
10108                 case "tl":
10109                     st.right = st.bottom = "0";
10110                     a = {width: zero, height: zero};
10111                 break;
10112                 case "bl":
10113                     st.right = st.top = "0";
10114                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10115                 break;
10116                 case "br":
10117                     st.left = st.top = "0";
10118                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10119                 break;
10120                 case "tr":
10121                     st.left = st.bottom = "0";
10122                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10123                 break;
10124             }
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 "easeOut", after);
10131         });
10132         return this;
10133     },
10134
10135         /**
10136          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10137          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10138          * The element must be removed from the DOM using the 'remove' config option if desired.
10139          * Usage:
10140          *<pre><code>
10141 // default
10142 el.puff();
10143
10144 // common config options shown with default values
10145 el.puff({
10146     easing: 'easeOut',
10147     duration: .5,
10148     remove: false,
10149     useDisplay: false
10150 });
10151 </code></pre>
10152          * @param {Object} options (optional) Object literal with any of the Fx config options
10153          * @return {Roo.Element} The Element
10154          */
10155     puff : function(o){
10156         var el = this.getFxEl();
10157         o = o || {};
10158
10159         el.queueFx(o, function(){
10160             this.clearOpacity();
10161             this.show();
10162
10163             // restore values after effect
10164             var r = this.getFxRestore();
10165             var st = this.dom.style;
10166
10167             var after = function(){
10168                 if(o.useDisplay){
10169                     el.setDisplayed(false);
10170                 }else{
10171                     el.hide();
10172                 }
10173
10174                 el.clearOpacity();
10175
10176                 el.setPositioning(r.pos);
10177                 st.width = r.width;
10178                 st.height = r.height;
10179                 st.fontSize = '';
10180                 el.afterFx(o);
10181             };
10182
10183             var width = this.getWidth();
10184             var height = this.getHeight();
10185
10186             arguments.callee.anim = this.fxanim({
10187                     width : {to: this.adjustWidth(width * 2)},
10188                     height : {to: this.adjustHeight(height * 2)},
10189                     points : {by: [-(width * .5), -(height * .5)]},
10190                     opacity : {to: 0},
10191                     fontSize: {to:200, unit: "%"}
10192                 },
10193                 o,
10194                 'motion',
10195                 .5,
10196                 "easeOut", after);
10197         });
10198         return this;
10199     },
10200
10201         /**
10202          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10203          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10204          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10205          * Usage:
10206          *<pre><code>
10207 // default
10208 el.switchOff();
10209
10210 // all config options shown with default values
10211 el.switchOff({
10212     easing: 'easeIn',
10213     duration: .3,
10214     remove: false,
10215     useDisplay: false
10216 });
10217 </code></pre>
10218          * @param {Object} options (optional) Object literal with any of the Fx config options
10219          * @return {Roo.Element} The Element
10220          */
10221     switchOff : function(o){
10222         var el = this.getFxEl();
10223         o = o || {};
10224
10225         el.queueFx(o, function(){
10226             this.clearOpacity();
10227             this.clip();
10228
10229             // restore values after effect
10230             var r = this.getFxRestore();
10231             var st = this.dom.style;
10232
10233             var after = function(){
10234                 if(o.useDisplay){
10235                     el.setDisplayed(false);
10236                 }else{
10237                     el.hide();
10238                 }
10239
10240                 el.clearOpacity();
10241                 el.setPositioning(r.pos);
10242                 st.width = r.width;
10243                 st.height = r.height;
10244
10245                 el.afterFx(o);
10246             };
10247
10248             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10249                 this.clearOpacity();
10250                 (function(){
10251                     this.fxanim({
10252                         height:{to:1},
10253                         points:{by:[0, this.getHeight() * .5]}
10254                     }, o, 'motion', 0.3, 'easeIn', after);
10255                 }).defer(100, this);
10256             });
10257         });
10258         return this;
10259     },
10260
10261     /**
10262      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10263      * changed using the "attr" config option) and then fading back to the original color. If no original
10264      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10265      * Usage:
10266 <pre><code>
10267 // default: highlight background to yellow
10268 el.highlight();
10269
10270 // custom: highlight foreground text to blue for 2 seconds
10271 el.highlight("0000ff", { attr: 'color', duration: 2 });
10272
10273 // common config options shown with default values
10274 el.highlight("ffff9c", {
10275     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10276     endColor: (current color) or "ffffff",
10277     easing: 'easeIn',
10278     duration: 1
10279 });
10280 </code></pre>
10281      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10282      * @param {Object} options (optional) Object literal with any of the Fx config options
10283      * @return {Roo.Element} The Element
10284      */ 
10285     highlight : function(color, o){
10286         var el = this.getFxEl();
10287         o = o || {};
10288
10289         el.queueFx(o, function(){
10290             color = color || "ffff9c";
10291             attr = o.attr || "backgroundColor";
10292
10293             this.clearOpacity();
10294             this.show();
10295
10296             var origColor = this.getColor(attr);
10297             var restoreColor = this.dom.style[attr];
10298             endColor = (o.endColor || origColor) || "ffffff";
10299
10300             var after = function(){
10301                 el.dom.style[attr] = restoreColor;
10302                 el.afterFx(o);
10303             };
10304
10305             var a = {};
10306             a[attr] = {from: color, to: endColor};
10307             arguments.callee.anim = this.fxanim(a,
10308                 o,
10309                 'color',
10310                 1,
10311                 'easeIn', after);
10312         });
10313         return this;
10314     },
10315
10316    /**
10317     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10318     * Usage:
10319 <pre><code>
10320 // default: a single light blue ripple
10321 el.frame();
10322
10323 // custom: 3 red ripples lasting 3 seconds total
10324 el.frame("ff0000", 3, { duration: 3 });
10325
10326 // common config options shown with default values
10327 el.frame("C3DAF9", 1, {
10328     duration: 1 //duration of entire animation (not each individual ripple)
10329     // Note: Easing is not configurable and will be ignored if included
10330 });
10331 </code></pre>
10332     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10333     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10334     * @param {Object} options (optional) Object literal with any of the Fx config options
10335     * @return {Roo.Element} The Element
10336     */
10337     frame : function(color, count, o){
10338         var el = this.getFxEl();
10339         o = o || {};
10340
10341         el.queueFx(o, function(){
10342             color = color || "#C3DAF9";
10343             if(color.length == 6){
10344                 color = "#" + color;
10345             }
10346             count = count || 1;
10347             duration = o.duration || 1;
10348             this.show();
10349
10350             var b = this.getBox();
10351             var animFn = function(){
10352                 var proxy = this.createProxy({
10353
10354                      style:{
10355                         visbility:"hidden",
10356                         position:"absolute",
10357                         "z-index":"35000", // yee haw
10358                         border:"0px solid " + color
10359                      }
10360                   });
10361                 var scale = Roo.isBorderBox ? 2 : 1;
10362                 proxy.animate({
10363                     top:{from:b.y, to:b.y - 20},
10364                     left:{from:b.x, to:b.x - 20},
10365                     borderWidth:{from:0, to:10},
10366                     opacity:{from:1, to:0},
10367                     height:{from:b.height, to:(b.height + (20*scale))},
10368                     width:{from:b.width, to:(b.width + (20*scale))}
10369                 }, duration, function(){
10370                     proxy.remove();
10371                 });
10372                 if(--count > 0){
10373                      animFn.defer((duration/2)*1000, this);
10374                 }else{
10375                     el.afterFx(o);
10376                 }
10377             };
10378             animFn.call(this);
10379         });
10380         return this;
10381     },
10382
10383    /**
10384     * Creates a pause before any subsequent queued effects begin.  If there are
10385     * no effects queued after the pause it will have no effect.
10386     * Usage:
10387 <pre><code>
10388 el.pause(1);
10389 </code></pre>
10390     * @param {Number} seconds The length of time to pause (in seconds)
10391     * @return {Roo.Element} The Element
10392     */
10393     pause : function(seconds){
10394         var el = this.getFxEl();
10395         var o = {};
10396
10397         el.queueFx(o, function(){
10398             setTimeout(function(){
10399                 el.afterFx(o);
10400             }, seconds * 1000);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10407     * using the "endOpacity" config option.
10408     * Usage:
10409 <pre><code>
10410 // default: fade in from opacity 0 to 100%
10411 el.fadeIn();
10412
10413 // custom: fade in from opacity 0 to 75% over 2 seconds
10414 el.fadeIn({ endOpacity: .75, duration: 2});
10415
10416 // common config options shown with default values
10417 el.fadeIn({
10418     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10419     easing: 'easeOut',
10420     duration: .5
10421 });
10422 </code></pre>
10423     * @param {Object} options (optional) Object literal with any of the Fx config options
10424     * @return {Roo.Element} The Element
10425     */
10426     fadeIn : function(o){
10427         var el = this.getFxEl();
10428         o = o || {};
10429         el.queueFx(o, function(){
10430             this.setOpacity(0);
10431             this.fixDisplay();
10432             this.dom.style.visibility = 'visible';
10433             var to = o.endOpacity || 1;
10434             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10435                 o, null, .5, "easeOut", function(){
10436                 if(to == 1){
10437                     this.clearOpacity();
10438                 }
10439                 el.afterFx(o);
10440             });
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade out from the element's current opacity to 0
10451 el.fadeOut();
10452
10453 // custom: fade out from the element's current opacity to 25% over 2 seconds
10454 el.fadeOut({ endOpacity: .25, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeOut({
10458     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461     remove: false,
10462     useDisplay: false
10463 });
10464 </code></pre>
10465     * @param {Object} options (optional) Object literal with any of the Fx config options
10466     * @return {Roo.Element} The Element
10467     */
10468     fadeOut : function(o){
10469         var el = this.getFxEl();
10470         o = o || {};
10471         el.queueFx(o, function(){
10472             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10473                 o, null, .5, "easeOut", function(){
10474                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10475                      this.dom.style.display = "none";
10476                 }else{
10477                      this.dom.style.visibility = "hidden";
10478                 }
10479                 this.clearOpacity();
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486    /**
10487     * Animates the transition of an element's dimensions from a starting height/width
10488     * to an ending height/width.
10489     * Usage:
10490 <pre><code>
10491 // change height and width to 100x100 pixels
10492 el.scale(100, 100);
10493
10494 // common config options shown with default values.  The height and width will default to
10495 // the element's existing values if passed as null.
10496 el.scale(
10497     [element's width],
10498     [element's height], {
10499     easing: 'easeOut',
10500     duration: .35
10501 });
10502 </code></pre>
10503     * @param {Number} width  The new width (pass undefined to keep the original width)
10504     * @param {Number} height  The new height (pass undefined to keep the original height)
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     scale : function(w, h, o){
10509         this.shift(Roo.apply({}, o, {
10510             width: w,
10511             height: h
10512         }));
10513         return this;
10514     },
10515
10516    /**
10517     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10518     * Any of these properties not specified in the config object will not be changed.  This effect 
10519     * requires that at least one new dimension, position or opacity setting must be passed in on
10520     * the config object in order for the function to have any effect.
10521     * Usage:
10522 <pre><code>
10523 // slide the element horizontally to x position 200 while changing the height and opacity
10524 el.shift({ x: 200, height: 50, opacity: .8 });
10525
10526 // common config options shown with default values.
10527 el.shift({
10528     width: [element's width],
10529     height: [element's height],
10530     x: [element's x position],
10531     y: [element's y position],
10532     opacity: [element's opacity],
10533     easing: 'easeOut',
10534     duration: .35
10535 });
10536 </code></pre>
10537     * @param {Object} options  Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     shift : function(o){
10541         var el = this.getFxEl();
10542         o = o || {};
10543         el.queueFx(o, function(){
10544             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10545             if(w !== undefined){
10546                 a.width = {to: this.adjustWidth(w)};
10547             }
10548             if(h !== undefined){
10549                 a.height = {to: this.adjustHeight(h)};
10550             }
10551             if(x !== undefined || y !== undefined){
10552                 a.points = {to: [
10553                     x !== undefined ? x : this.getX(),
10554                     y !== undefined ? y : this.getY()
10555                 ]};
10556             }
10557             if(op !== undefined){
10558                 a.opacity = {to: op};
10559             }
10560             if(o.xy !== undefined){
10561                 a.points = {to: o.xy};
10562             }
10563             arguments.callee.anim = this.fxanim(a,
10564                 o, 'motion', .35, "easeOut", function(){
10565                 el.afterFx(o);
10566             });
10567         });
10568         return this;
10569     },
10570
10571         /**
10572          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10573          * ending point of the effect.
10574          * Usage:
10575          *<pre><code>
10576 // default: slide the element downward while fading out
10577 el.ghost();
10578
10579 // custom: slide the element out to the right with a 2-second duration
10580 el.ghost('r', { duration: 2 });
10581
10582 // common config options shown with default values
10583 el.ghost('b', {
10584     easing: 'easeOut',
10585     duration: .5
10586     remove: false,
10587     useDisplay: false
10588 });
10589 </code></pre>
10590          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10591          * @param {Object} options (optional) Object literal with any of the Fx config options
10592          * @return {Roo.Element} The Element
10593          */
10594     ghost : function(anchor, o){
10595         var el = this.getFxEl();
10596         o = o || {};
10597
10598         el.queueFx(o, function(){
10599             anchor = anchor || "b";
10600
10601             // restore values after effect
10602             var r = this.getFxRestore();
10603             var w = this.getWidth(),
10604                 h = this.getHeight();
10605
10606             var st = this.dom.style;
10607
10608             var after = function(){
10609                 if(o.useDisplay){
10610                     el.setDisplayed(false);
10611                 }else{
10612                     el.hide();
10613                 }
10614
10615                 el.clearOpacity();
10616                 el.setPositioning(r.pos);
10617                 st.width = r.width;
10618                 st.height = r.height;
10619
10620                 el.afterFx(o);
10621             };
10622
10623             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10624             switch(anchor.toLowerCase()){
10625                 case "t":
10626                     pt.by = [0, -h];
10627                 break;
10628                 case "l":
10629                     pt.by = [-w, 0];
10630                 break;
10631                 case "r":
10632                     pt.by = [w, 0];
10633                 break;
10634                 case "b":
10635                     pt.by = [0, h];
10636                 break;
10637                 case "tl":
10638                     pt.by = [-w, -h];
10639                 break;
10640                 case "bl":
10641                     pt.by = [-w, h];
10642                 break;
10643                 case "br":
10644                     pt.by = [w, h];
10645                 break;
10646                 case "tr":
10647                     pt.by = [w, -h];
10648                 break;
10649             }
10650
10651             arguments.callee.anim = this.fxanim(a,
10652                 o,
10653                 'motion',
10654                 .5,
10655                 "easeOut", after);
10656         });
10657         return this;
10658     },
10659
10660         /**
10661          * Ensures that all effects queued after syncFx is called on the element are
10662          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10663          * @return {Roo.Element} The Element
10664          */
10665     syncFx : function(){
10666         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10667             block : false,
10668             concurrent : true,
10669             stopFx : false
10670         });
10671         return this;
10672     },
10673
10674         /**
10675          * Ensures that all effects queued after sequenceFx is called on the element are
10676          * run in sequence.  This is the opposite of {@link #syncFx}.
10677          * @return {Roo.Element} The Element
10678          */
10679     sequenceFx : function(){
10680         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10681             block : false,
10682             concurrent : false,
10683             stopFx : false
10684         });
10685         return this;
10686     },
10687
10688         /* @private */
10689     nextFx : function(){
10690         var ef = this.fxQueue[0];
10691         if(ef){
10692             ef.call(this);
10693         }
10694     },
10695
10696         /**
10697          * Returns true if the element has any effects actively running or queued, else returns false.
10698          * @return {Boolean} True if element has active effects, else false
10699          */
10700     hasActiveFx : function(){
10701         return this.fxQueue && this.fxQueue[0];
10702     },
10703
10704         /**
10705          * Stops any running effects and clears the element's internal effects queue if it contains
10706          * any additional effects that haven't started yet.
10707          * @return {Roo.Element} The Element
10708          */
10709     stopFx : function(){
10710         if(this.hasActiveFx()){
10711             var cur = this.fxQueue[0];
10712             if(cur && cur.anim && cur.anim.isAnimated()){
10713                 this.fxQueue = [cur]; // clear out others
10714                 cur.anim.stop(true);
10715             }
10716         }
10717         return this;
10718     },
10719
10720         /* @private */
10721     beforeFx : function(o){
10722         if(this.hasActiveFx() && !o.concurrent){
10723            if(o.stopFx){
10724                this.stopFx();
10725                return true;
10726            }
10727            return false;
10728         }
10729         return true;
10730     },
10731
10732         /**
10733          * Returns true if the element is currently blocking so that no other effect can be queued
10734          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10735          * used to ensure that an effect initiated by a user action runs to completion prior to the
10736          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10737          * @return {Boolean} True if blocking, else false
10738          */
10739     hasFxBlock : function(){
10740         var q = this.fxQueue;
10741         return q && q[0] && q[0].block;
10742     },
10743
10744         /* @private */
10745     queueFx : function(o, fn){
10746         if(!this.fxQueue){
10747             this.fxQueue = [];
10748         }
10749         if(!this.hasFxBlock()){
10750             Roo.applyIf(o, this.fxDefaults);
10751             if(!o.concurrent){
10752                 var run = this.beforeFx(o);
10753                 fn.block = o.block;
10754                 this.fxQueue.push(fn);
10755                 if(run){
10756                     this.nextFx();
10757                 }
10758             }else{
10759                 fn.call(this);
10760             }
10761         }
10762         return this;
10763     },
10764
10765         /* @private */
10766     fxWrap : function(pos, o, vis){
10767         var wrap;
10768         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10769             var wrapXY;
10770             if(o.fixPosition){
10771                 wrapXY = this.getXY();
10772             }
10773             var div = document.createElement("div");
10774             div.style.visibility = vis;
10775             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10776             wrap.setPositioning(pos);
10777             if(wrap.getStyle("position") == "static"){
10778                 wrap.position("relative");
10779             }
10780             this.clearPositioning('auto');
10781             wrap.clip();
10782             wrap.dom.appendChild(this.dom);
10783             if(wrapXY){
10784                 wrap.setXY(wrapXY);
10785             }
10786         }
10787         return wrap;
10788     },
10789
10790         /* @private */
10791     fxUnwrap : function(wrap, pos, o){
10792         this.clearPositioning();
10793         this.setPositioning(pos);
10794         if(!o.wrap){
10795             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10796             wrap.remove();
10797         }
10798     },
10799
10800         /* @private */
10801     getFxRestore : function(){
10802         var st = this.dom.style;
10803         return {pos: this.getPositioning(), width: st.width, height : st.height};
10804     },
10805
10806         /* @private */
10807     afterFx : function(o){
10808         if(o.afterStyle){
10809             this.applyStyles(o.afterStyle);
10810         }
10811         if(o.afterCls){
10812             this.addClass(o.afterCls);
10813         }
10814         if(o.remove === true){
10815             this.remove();
10816         }
10817         Roo.callback(o.callback, o.scope, [this]);
10818         if(!o.concurrent){
10819             this.fxQueue.shift();
10820             this.nextFx();
10821         }
10822     },
10823
10824         /* @private */
10825     getFxEl : function(){ // support for composite element fx
10826         return Roo.get(this.dom);
10827     },
10828
10829         /* @private */
10830     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10831         animType = animType || 'run';
10832         opt = opt || {};
10833         var anim = Roo.lib.Anim[animType](
10834             this.dom, args,
10835             (opt.duration || defaultDur) || .35,
10836             (opt.easing || defaultEase) || 'easeOut',
10837             function(){
10838                 Roo.callback(cb, this);
10839             },
10840             this
10841         );
10842         opt.anim = anim;
10843         return anim;
10844     }
10845 };
10846
10847 // backwords compat
10848 Roo.Fx.resize = Roo.Fx.scale;
10849
10850 //When included, Roo.Fx is automatically applied to Element so that all basic
10851 //effects are available directly via the Element API
10852 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10853  * Based on:
10854  * Ext JS Library 1.1.1
10855  * Copyright(c) 2006-2007, Ext JS, LLC.
10856  *
10857  * Originally Released Under LGPL - original licence link has changed is not relivant.
10858  *
10859  * Fork - LGPL
10860  * <script type="text/javascript">
10861  */
10862
10863
10864 /**
10865  * @class Roo.CompositeElement
10866  * Standard composite class. Creates a Roo.Element for every element in the collection.
10867  * <br><br>
10868  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10869  * actions will be performed on all the elements in this collection.</b>
10870  * <br><br>
10871  * All methods return <i>this</i> and can be chained.
10872  <pre><code>
10873  var els = Roo.select("#some-el div.some-class", true);
10874  // or select directly from an existing element
10875  var el = Roo.get('some-el');
10876  el.select('div.some-class', true);
10877
10878  els.setWidth(100); // all elements become 100 width
10879  els.hide(true); // all elements fade out and hide
10880  // or
10881  els.setWidth(100).hide(true);
10882  </code></pre>
10883  */
10884 Roo.CompositeElement = function(els){
10885     this.elements = [];
10886     this.addElements(els);
10887 };
10888 Roo.CompositeElement.prototype = {
10889     isComposite: true,
10890     addElements : function(els){
10891         if(!els) return this;
10892         if(typeof els == "string"){
10893             els = Roo.Element.selectorFunction(els);
10894         }
10895         var yels = this.elements;
10896         var index = yels.length-1;
10897         for(var i = 0, len = els.length; i < len; i++) {
10898                 yels[++index] = Roo.get(els[i]);
10899         }
10900         return this;
10901     },
10902
10903     /**
10904     * Clears this composite and adds the elements returned by the passed selector.
10905     * @param {String/Array} els A string CSS selector, an array of elements or an element
10906     * @return {CompositeElement} this
10907     */
10908     fill : function(els){
10909         this.elements = [];
10910         this.add(els);
10911         return this;
10912     },
10913
10914     /**
10915     * Filters this composite to only elements that match the passed selector.
10916     * @param {String} selector A string CSS selector
10917     * @return {CompositeElement} this
10918     */
10919     filter : function(selector){
10920         var els = [];
10921         this.each(function(el){
10922             if(el.is(selector)){
10923                 els[els.length] = el.dom;
10924             }
10925         });
10926         this.fill(els);
10927         return this;
10928     },
10929
10930     invoke : function(fn, args){
10931         var els = this.elements;
10932         for(var i = 0, len = els.length; i < len; i++) {
10933                 Roo.Element.prototype[fn].apply(els[i], args);
10934         }
10935         return this;
10936     },
10937     /**
10938     * Adds elements to this composite.
10939     * @param {String/Array} els A string CSS selector, an array of elements or an element
10940     * @return {CompositeElement} this
10941     */
10942     add : function(els){
10943         if(typeof els == "string"){
10944             this.addElements(Roo.Element.selectorFunction(els));
10945         }else if(els.length !== undefined){
10946             this.addElements(els);
10947         }else{
10948             this.addElements([els]);
10949         }
10950         return this;
10951     },
10952     /**
10953     * Calls the passed function passing (el, this, index) for each element in this composite.
10954     * @param {Function} fn The function to call
10955     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10956     * @return {CompositeElement} this
10957     */
10958     each : function(fn, scope){
10959         var els = this.elements;
10960         for(var i = 0, len = els.length; i < len; i++){
10961             if(fn.call(scope || els[i], els[i], this, i) === false) {
10962                 break;
10963             }
10964         }
10965         return this;
10966     },
10967
10968     /**
10969      * Returns the Element object at the specified index
10970      * @param {Number} index
10971      * @return {Roo.Element}
10972      */
10973     item : function(index){
10974         return this.elements[index] || null;
10975     },
10976
10977     /**
10978      * Returns the first Element
10979      * @return {Roo.Element}
10980      */
10981     first : function(){
10982         return this.item(0);
10983     },
10984
10985     /**
10986      * Returns the last Element
10987      * @return {Roo.Element}
10988      */
10989     last : function(){
10990         return this.item(this.elements.length-1);
10991     },
10992
10993     /**
10994      * Returns the number of elements in this composite
10995      * @return Number
10996      */
10997     getCount : function(){
10998         return this.elements.length;
10999     },
11000
11001     /**
11002      * Returns true if this composite contains the passed element
11003      * @return Boolean
11004      */
11005     contains : function(el){
11006         return this.indexOf(el) !== -1;
11007     },
11008
11009     /**
11010      * Returns true if this composite contains the passed element
11011      * @return Boolean
11012      */
11013     indexOf : function(el){
11014         return this.elements.indexOf(Roo.get(el));
11015     },
11016
11017
11018     /**
11019     * Removes the specified element(s).
11020     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11021     * or an array of any of those.
11022     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11023     * @return {CompositeElement} this
11024     */
11025     removeElement : function(el, removeDom){
11026         if(el instanceof Array){
11027             for(var i = 0, len = el.length; i < len; i++){
11028                 this.removeElement(el[i]);
11029             }
11030             return this;
11031         }
11032         var index = typeof el == 'number' ? el : this.indexOf(el);
11033         if(index !== -1){
11034             if(removeDom){
11035                 var d = this.elements[index];
11036                 if(d.dom){
11037                     d.remove();
11038                 }else{
11039                     d.parentNode.removeChild(d);
11040                 }
11041             }
11042             this.elements.splice(index, 1);
11043         }
11044         return this;
11045     },
11046
11047     /**
11048     * Replaces the specified element with the passed element.
11049     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11050     * to replace.
11051     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11052     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11053     * @return {CompositeElement} this
11054     */
11055     replaceElement : function(el, replacement, domReplace){
11056         var index = typeof el == 'number' ? el : this.indexOf(el);
11057         if(index !== -1){
11058             if(domReplace){
11059                 this.elements[index].replaceWith(replacement);
11060             }else{
11061                 this.elements.splice(index, 1, Roo.get(replacement))
11062             }
11063         }
11064         return this;
11065     },
11066
11067     /**
11068      * Removes all elements.
11069      */
11070     clear : function(){
11071         this.elements = [];
11072     }
11073 };
11074 (function(){
11075     Roo.CompositeElement.createCall = function(proto, fnName){
11076         if(!proto[fnName]){
11077             proto[fnName] = function(){
11078                 return this.invoke(fnName, arguments);
11079             };
11080         }
11081     };
11082     for(var fnName in Roo.Element.prototype){
11083         if(typeof Roo.Element.prototype[fnName] == "function"){
11084             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11085         }
11086     };
11087 })();
11088 /*
11089  * Based on:
11090  * Ext JS Library 1.1.1
11091  * Copyright(c) 2006-2007, Ext JS, LLC.
11092  *
11093  * Originally Released Under LGPL - original licence link has changed is not relivant.
11094  *
11095  * Fork - LGPL
11096  * <script type="text/javascript">
11097  */
11098
11099 /**
11100  * @class Roo.CompositeElementLite
11101  * @extends Roo.CompositeElement
11102  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11103  <pre><code>
11104  var els = Roo.select("#some-el div.some-class");
11105  // or select directly from an existing element
11106  var el = Roo.get('some-el');
11107  el.select('div.some-class');
11108
11109  els.setWidth(100); // all elements become 100 width
11110  els.hide(true); // all elements fade out and hide
11111  // or
11112  els.setWidth(100).hide(true);
11113  </code></pre><br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  */
11117 Roo.CompositeElementLite = function(els){
11118     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11119     this.el = new Roo.Element.Flyweight();
11120 };
11121 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11122     addElements : function(els){
11123         if(els){
11124             if(els instanceof Array){
11125                 this.elements = this.elements.concat(els);
11126             }else{
11127                 var yels = this.elements;
11128                 var index = yels.length-1;
11129                 for(var i = 0, len = els.length; i < len; i++) {
11130                     yels[++index] = els[i];
11131                 }
11132             }
11133         }
11134         return this;
11135     },
11136     invoke : function(fn, args){
11137         var els = this.elements;
11138         var el = this.el;
11139         for(var i = 0, len = els.length; i < len; i++) {
11140             el.dom = els[i];
11141                 Roo.Element.prototype[fn].apply(el, args);
11142         }
11143         return this;
11144     },
11145     /**
11146      * Returns a flyweight Element of the dom element object at the specified index
11147      * @param {Number} index
11148      * @return {Roo.Element}
11149      */
11150     item : function(index){
11151         if(!this.elements[index]){
11152             return null;
11153         }
11154         this.el.dom = this.elements[index];
11155         return this.el;
11156     },
11157
11158     // fixes scope with flyweight
11159     addListener : function(eventName, handler, scope, opt){
11160         var els = this.elements;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11163         }
11164         return this;
11165     },
11166
11167     /**
11168     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11169     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11170     * a reference to the dom node, use el.dom.</b>
11171     * @param {Function} fn The function to call
11172     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11173     * @return {CompositeElement} this
11174     */
11175     each : function(fn, scope){
11176         var els = this.elements;
11177         var el = this.el;
11178         for(var i = 0, len = els.length; i < len; i++){
11179             el.dom = els[i];
11180                 if(fn.call(scope || el, el, this, i) === false){
11181                 break;
11182             }
11183         }
11184         return this;
11185     },
11186
11187     indexOf : function(el){
11188         return this.elements.indexOf(Roo.getDom(el));
11189     },
11190
11191     replaceElement : function(el, replacement, domReplace){
11192         var index = typeof el == 'number' ? el : this.indexOf(el);
11193         if(index !== -1){
11194             replacement = Roo.getDom(replacement);
11195             if(domReplace){
11196                 var d = this.elements[index];
11197                 d.parentNode.insertBefore(replacement, d);
11198                 d.parentNode.removeChild(d);
11199             }
11200             this.elements.splice(index, 1, replacement);
11201         }
11202         return this;
11203     }
11204 });
11205 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11206
11207 /*
11208  * Based on:
11209  * Ext JS Library 1.1.1
11210  * Copyright(c) 2006-2007, Ext JS, LLC.
11211  *
11212  * Originally Released Under LGPL - original licence link has changed is not relivant.
11213  *
11214  * Fork - LGPL
11215  * <script type="text/javascript">
11216  */
11217
11218  
11219
11220 /**
11221  * @class Roo.data.Connection
11222  * @extends Roo.util.Observable
11223  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11224  * either to a configured URL, or to a URL specified at request time.<br><br>
11225  * <p>
11226  * Requests made by this class are asynchronous, and will return immediately. No data from
11227  * the server will be available to the statement immediately following the {@link #request} call.
11228  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11229  * <p>
11230  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11231  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11232  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11233  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11234  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11235  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11236  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11237  * standard DOM methods.
11238  * @constructor
11239  * @param {Object} config a configuration object.
11240  */
11241 Roo.data.Connection = function(config){
11242     Roo.apply(this, config);
11243     this.addEvents({
11244         /**
11245          * @event beforerequest
11246          * Fires before a network request is made to retrieve a data object.
11247          * @param {Connection} conn This Connection object.
11248          * @param {Object} options The options config object passed to the {@link #request} method.
11249          */
11250         "beforerequest" : true,
11251         /**
11252          * @event requestcomplete
11253          * Fires if the request was successfully completed.
11254          * @param {Connection} conn This Connection object.
11255          * @param {Object} response The XHR object containing the response data.
11256          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11257          * @param {Object} options The options config object passed to the {@link #request} method.
11258          */
11259         "requestcomplete" : true,
11260         /**
11261          * @event requestexception
11262          * Fires if an error HTTP status was returned from the server.
11263          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11264          * @param {Connection} conn This Connection object.
11265          * @param {Object} response The XHR object containing the response data.
11266          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11267          * @param {Object} options The options config object passed to the {@link #request} method.
11268          */
11269         "requestexception" : true
11270     });
11271     Roo.data.Connection.superclass.constructor.call(this);
11272 };
11273
11274 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11275     /**
11276      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11277      */
11278     /**
11279      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11280      * extra parameters to each request made by this object. (defaults to undefined)
11281      */
11282     /**
11283      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11284      *  to each request made by this object. (defaults to undefined)
11285      */
11286     /**
11287      * @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)
11288      */
11289     /**
11290      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11291      */
11292     timeout : 30000,
11293     /**
11294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11295      * @type Boolean
11296      */
11297     autoAbort:false,
11298
11299     /**
11300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11301      * @type Boolean
11302      */
11303     disableCaching: true,
11304
11305     /**
11306      * Sends an HTTP request to a remote server.
11307      * @param {Object} options An object which may contain the following properties:<ul>
11308      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11309      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11310      * request, a url encoded string or a function to call to get either.</li>
11311      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11312      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11313      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11314      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11315      * <li>options {Object} The parameter to the request call.</li>
11316      * <li>success {Boolean} True if the request succeeded.</li>
11317      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11318      * </ul></li>
11319      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11320      * The callback is passed the following parameters:<ul>
11321      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11322      * <li>options {Object} The parameter to the request call.</li>
11323      * </ul></li>
11324      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11325      * The callback is passed the following parameters:<ul>
11326      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11327      * <li>options {Object} The parameter to the request call.</li>
11328      * </ul></li>
11329      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11330      * for the callback function. Defaults to the browser window.</li>
11331      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11332      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11333      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11334      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11335      * params for the post data. Any params will be appended to the URL.</li>
11336      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11337      * </ul>
11338      * @return {Number} transactionId
11339      */
11340     request : function(o){
11341         if(this.fireEvent("beforerequest", this, o) !== false){
11342             var p = o.params;
11343
11344             if(typeof p == "function"){
11345                 p = p.call(o.scope||window, o);
11346             }
11347             if(typeof p == "object"){
11348                 p = Roo.urlEncode(o.params);
11349             }
11350             if(this.extraParams){
11351                 var extras = Roo.urlEncode(this.extraParams);
11352                 p = p ? (p + '&' + extras) : extras;
11353             }
11354
11355             var url = o.url || this.url;
11356             if(typeof url == 'function'){
11357                 url = url.call(o.scope||window, o);
11358             }
11359
11360             if(o.form){
11361                 var form = Roo.getDom(o.form);
11362                 url = url || form.action;
11363
11364                 var enctype = form.getAttribute("enctype");
11365                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11366                     return this.doFormUpload(o, p, url);
11367                 }
11368                 var f = Roo.lib.Ajax.serializeForm(form);
11369                 p = p ? (p + '&' + f) : f;
11370             }
11371
11372             var hs = o.headers;
11373             if(this.defaultHeaders){
11374                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11375                 if(!o.headers){
11376                     o.headers = hs;
11377                 }
11378             }
11379
11380             var cb = {
11381                 success: this.handleResponse,
11382                 failure: this.handleFailure,
11383                 scope: this,
11384                 argument: {options: o},
11385                 timeout : this.timeout
11386             };
11387
11388             var method = o.method||this.method||(p ? "POST" : "GET");
11389
11390             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11391                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11392             }
11393
11394             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11395                 if(o.autoAbort){
11396                     this.abort();
11397                 }
11398             }else if(this.autoAbort !== false){
11399                 this.abort();
11400             }
11401
11402             if((method == 'GET' && p) || o.xmlData){
11403                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11404                 p = '';
11405             }
11406             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11407             return this.transId;
11408         }else{
11409             Roo.callback(o.callback, o.scope, [o, null, null]);
11410             return null;
11411         }
11412     },
11413
11414     /**
11415      * Determine whether this object has a request outstanding.
11416      * @param {Number} transactionId (Optional) defaults to the last transaction
11417      * @return {Boolean} True if there is an outstanding request.
11418      */
11419     isLoading : function(transId){
11420         if(transId){
11421             return Roo.lib.Ajax.isCallInProgress(transId);
11422         }else{
11423             return this.transId ? true : false;
11424         }
11425     },
11426
11427     /**
11428      * Aborts any outstanding request.
11429      * @param {Number} transactionId (Optional) defaults to the last transaction
11430      */
11431     abort : function(transId){
11432         if(transId || this.isLoading()){
11433             Roo.lib.Ajax.abort(transId || this.transId);
11434         }
11435     },
11436
11437     // private
11438     handleResponse : function(response){
11439         this.transId = false;
11440         var options = response.argument.options;
11441         response.argument = options ? options.argument : null;
11442         this.fireEvent("requestcomplete", this, response, options);
11443         Roo.callback(options.success, options.scope, [response, options]);
11444         Roo.callback(options.callback, options.scope, [options, true, response]);
11445     },
11446
11447     // private
11448     handleFailure : function(response, e){
11449         this.transId = false;
11450         var options = response.argument.options;
11451         response.argument = options ? options.argument : null;
11452         this.fireEvent("requestexception", this, response, options, e);
11453         Roo.callback(options.failure, options.scope, [response, options]);
11454         Roo.callback(options.callback, options.scope, [options, false, response]);
11455     },
11456
11457     // private
11458     doFormUpload : function(o, ps, url){
11459         var id = Roo.id();
11460         var frame = document.createElement('iframe');
11461         frame.id = id;
11462         frame.name = id;
11463         frame.className = 'x-hidden';
11464         if(Roo.isIE){
11465             frame.src = Roo.SSL_SECURE_URL;
11466         }
11467         document.body.appendChild(frame);
11468
11469         if(Roo.isIE){
11470            document.frames[id].name = id;
11471         }
11472
11473         var form = Roo.getDom(o.form);
11474         form.target = id;
11475         form.method = 'POST';
11476         form.enctype = form.encoding = 'multipart/form-data';
11477         if(url){
11478             form.action = url;
11479         }
11480
11481         var hiddens, hd;
11482         if(ps){ // add dynamic params
11483             hiddens = [];
11484             ps = Roo.urlDecode(ps, false);
11485             for(var k in ps){
11486                 if(ps.hasOwnProperty(k)){
11487                     hd = document.createElement('input');
11488                     hd.type = 'hidden';
11489                     hd.name = k;
11490                     hd.value = ps[k];
11491                     form.appendChild(hd);
11492                     hiddens.push(hd);
11493                 }
11494             }
11495         }
11496
11497         function cb(){
11498             var r = {  // bogus response object
11499                 responseText : '',
11500                 responseXML : null
11501             };
11502
11503             r.argument = o ? o.argument : null;
11504
11505             try { //
11506                 var doc;
11507                 if(Roo.isIE){
11508                     doc = frame.contentWindow.document;
11509                 }else {
11510                     doc = (frame.contentDocument || window.frames[id].document);
11511                 }
11512                 if(doc && doc.body){
11513                     r.responseText = doc.body.innerHTML;
11514                 }
11515                 if(doc && doc.XMLDocument){
11516                     r.responseXML = doc.XMLDocument;
11517                 }else {
11518                     r.responseXML = doc;
11519                 }
11520             }
11521             catch(e) {
11522                 // ignore
11523             }
11524
11525             Roo.EventManager.removeListener(frame, 'load', cb, this);
11526
11527             this.fireEvent("requestcomplete", this, r, o);
11528             Roo.callback(o.success, o.scope, [r, o]);
11529             Roo.callback(o.callback, o.scope, [o, true, r]);
11530
11531             setTimeout(function(){document.body.removeChild(frame);}, 100);
11532         }
11533
11534         Roo.EventManager.on(frame, 'load', cb, this);
11535         form.submit();
11536
11537         if(hiddens){ // remove dynamic params
11538             for(var i = 0, len = hiddens.length; i < len; i++){
11539                 form.removeChild(hiddens[i]);
11540             }
11541         }
11542     }
11543 });
11544
11545 /**
11546  * @class Roo.Ajax
11547  * @extends Roo.data.Connection
11548  * Global Ajax request class.
11549  *
11550  * @singleton
11551  */
11552 Roo.Ajax = new Roo.data.Connection({
11553     // fix up the docs
11554    /**
11555      * @cfg {String} url @hide
11556      */
11557     /**
11558      * @cfg {Object} extraParams @hide
11559      */
11560     /**
11561      * @cfg {Object} defaultHeaders @hide
11562      */
11563     /**
11564      * @cfg {String} method (Optional) @hide
11565      */
11566     /**
11567      * @cfg {Number} timeout (Optional) @hide
11568      */
11569     /**
11570      * @cfg {Boolean} autoAbort (Optional) @hide
11571      */
11572
11573     /**
11574      * @cfg {Boolean} disableCaching (Optional) @hide
11575      */
11576
11577     /**
11578      * @property  disableCaching
11579      * True to add a unique cache-buster param to GET requests. (defaults to true)
11580      * @type Boolean
11581      */
11582     /**
11583      * @property  url
11584      * The default URL to be used for requests to the server. (defaults to undefined)
11585      * @type String
11586      */
11587     /**
11588      * @property  extraParams
11589      * An object containing properties which are used as
11590      * extra parameters to each request made by this object. (defaults to undefined)
11591      * @type Object
11592      */
11593     /**
11594      * @property  defaultHeaders
11595      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11596      * @type Object
11597      */
11598     /**
11599      * @property  method
11600      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11601      * @type String
11602      */
11603     /**
11604      * @property  timeout
11605      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11606      * @type Number
11607      */
11608
11609     /**
11610      * @property  autoAbort
11611      * Whether a new request should abort any pending requests. (defaults to false)
11612      * @type Boolean
11613      */
11614     autoAbort : false,
11615
11616     /**
11617      * Serialize the passed form into a url encoded string
11618      * @param {String/HTMLElement} form
11619      * @return {String}
11620      */
11621     serializeForm : function(form){
11622         return Roo.lib.Ajax.serializeForm(form);
11623     }
11624 });/*
11625  * Based on:
11626  * Ext JS Library 1.1.1
11627  * Copyright(c) 2006-2007, Ext JS, LLC.
11628  *
11629  * Originally Released Under LGPL - original licence link has changed is not relivant.
11630  *
11631  * Fork - LGPL
11632  * <script type="text/javascript">
11633  */
11634  
11635 /**
11636  * Global Ajax request class.
11637  * 
11638  * @class Roo.Ajax
11639  * @extends Roo.data.Connection
11640  * @static
11641  * 
11642  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11643  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11644  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11645  * @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)
11646  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11647  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11648  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11649  */
11650 Roo.Ajax = new Roo.data.Connection({
11651     // fix up the docs
11652     /**
11653      * @scope Roo.Ajax
11654      * @type {Boolear} 
11655      */
11656     autoAbort : false,
11657
11658     /**
11659      * Serialize the passed form into a url encoded string
11660      * @scope Roo.Ajax
11661      * @param {String/HTMLElement} form
11662      * @return {String}
11663      */
11664     serializeForm : function(form){
11665         return Roo.lib.Ajax.serializeForm(form);
11666     }
11667 });/*
11668  * Based on:
11669  * Ext JS Library 1.1.1
11670  * Copyright(c) 2006-2007, Ext JS, LLC.
11671  *
11672  * Originally Released Under LGPL - original licence link has changed is not relivant.
11673  *
11674  * Fork - LGPL
11675  * <script type="text/javascript">
11676  */
11677
11678  
11679 /**
11680  * @class Roo.UpdateManager
11681  * @extends Roo.util.Observable
11682  * Provides AJAX-style update for Element object.<br><br>
11683  * Usage:<br>
11684  * <pre><code>
11685  * // Get it from a Roo.Element object
11686  * var el = Roo.get("foo");
11687  * var mgr = el.getUpdateManager();
11688  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11689  * ...
11690  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11691  * <br>
11692  * // or directly (returns the same UpdateManager instance)
11693  * var mgr = new Roo.UpdateManager("myElementId");
11694  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11695  * mgr.on("update", myFcnNeedsToKnow);
11696  * <br>
11697    // short handed call directly from the element object
11698    Roo.get("foo").load({
11699         url: "bar.php",
11700         scripts:true,
11701         params: "for=bar",
11702         text: "Loading Foo..."
11703    });
11704  * </code></pre>
11705  * @constructor
11706  * Create new UpdateManager directly.
11707  * @param {String/HTMLElement/Roo.Element} el The element to update
11708  * @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).
11709  */
11710 Roo.UpdateManager = function(el, forceNew){
11711     el = Roo.get(el);
11712     if(!forceNew && el.updateManager){
11713         return el.updateManager;
11714     }
11715     /**
11716      * The Element object
11717      * @type Roo.Element
11718      */
11719     this.el = el;
11720     /**
11721      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11722      * @type String
11723      */
11724     this.defaultUrl = null;
11725
11726     this.addEvents({
11727         /**
11728          * @event beforeupdate
11729          * Fired before an update is made, return false from your handler and the update is cancelled.
11730          * @param {Roo.Element} el
11731          * @param {String/Object/Function} url
11732          * @param {String/Object} params
11733          */
11734         "beforeupdate": true,
11735         /**
11736          * @event update
11737          * Fired after successful update is made.
11738          * @param {Roo.Element} el
11739          * @param {Object} oResponseObject The response Object
11740          */
11741         "update": true,
11742         /**
11743          * @event failure
11744          * Fired on update failure.
11745          * @param {Roo.Element} el
11746          * @param {Object} oResponseObject The response Object
11747          */
11748         "failure": true
11749     });
11750     var d = Roo.UpdateManager.defaults;
11751     /**
11752      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11753      * @type String
11754      */
11755     this.sslBlankUrl = d.sslBlankUrl;
11756     /**
11757      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11758      * @type Boolean
11759      */
11760     this.disableCaching = d.disableCaching;
11761     /**
11762      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11763      * @type String
11764      */
11765     this.indicatorText = d.indicatorText;
11766     /**
11767      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11768      * @type String
11769      */
11770     this.showLoadIndicator = d.showLoadIndicator;
11771     /**
11772      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11773      * @type Number
11774      */
11775     this.timeout = d.timeout;
11776
11777     /**
11778      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11779      * @type Boolean
11780      */
11781     this.loadScripts = d.loadScripts;
11782
11783     /**
11784      * Transaction object of current executing transaction
11785      */
11786     this.transaction = null;
11787
11788     /**
11789      * @private
11790      */
11791     this.autoRefreshProcId = null;
11792     /**
11793      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11794      * @type Function
11795      */
11796     this.refreshDelegate = this.refresh.createDelegate(this);
11797     /**
11798      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11799      * @type Function
11800      */
11801     this.updateDelegate = this.update.createDelegate(this);
11802     /**
11803      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11804      * @type Function
11805      */
11806     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11807     /**
11808      * @private
11809      */
11810     this.successDelegate = this.processSuccess.createDelegate(this);
11811     /**
11812      * @private
11813      */
11814     this.failureDelegate = this.processFailure.createDelegate(this);
11815
11816     if(!this.renderer){
11817      /**
11818       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11819       */
11820     this.renderer = new Roo.UpdateManager.BasicRenderer();
11821     }
11822     
11823     Roo.UpdateManager.superclass.constructor.call(this);
11824 };
11825
11826 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11827     /**
11828      * Get the Element this UpdateManager is bound to
11829      * @return {Roo.Element} The element
11830      */
11831     getEl : function(){
11832         return this.el;
11833     },
11834     /**
11835      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11836      * @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:
11837 <pre><code>
11838 um.update({<br/>
11839     url: "your-url.php",<br/>
11840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11841     callback: yourFunction,<br/>
11842     scope: yourObject, //(optional scope)  <br/>
11843     discardUrl: false, <br/>
11844     nocache: false,<br/>
11845     text: "Loading...",<br/>
11846     timeout: 30,<br/>
11847     scripts: false<br/>
11848 });
11849 </code></pre>
11850      * The only required property is url. The optional properties nocache, text and scripts
11851      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11852      * @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}
11853      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11854      * @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.
11855      */
11856     update : function(url, params, callback, discardUrl){
11857         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11858             var method = this.method, cfg;
11859             if(typeof url == "object"){ // must be config object
11860                 cfg = url;
11861                 url = cfg.url;
11862                 params = params || cfg.params;
11863                 callback = callback || cfg.callback;
11864                 discardUrl = discardUrl || cfg.discardUrl;
11865                 if(callback && cfg.scope){
11866                     callback = callback.createDelegate(cfg.scope);
11867                 }
11868                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11869                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11870                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11871                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11872                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11873             }
11874             this.showLoading();
11875             if(!discardUrl){
11876                 this.defaultUrl = url;
11877             }
11878             if(typeof url == "function"){
11879                 url = url.call(this);
11880             }
11881
11882             method = method || (params ? "POST" : "GET");
11883             if(method == "GET"){
11884                 url = this.prepareUrl(url);
11885             }
11886
11887             var o = Roo.apply(cfg ||{}, {
11888                 url : url,
11889                 params: params,
11890                 success: this.successDelegate,
11891                 failure: this.failureDelegate,
11892                 callback: undefined,
11893                 timeout: (this.timeout*1000),
11894                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11895             });
11896
11897             this.transaction = Roo.Ajax.request(o);
11898         }
11899     },
11900
11901     /**
11902      * 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.
11903      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11904      * @param {String/HTMLElement} form The form Id or form element
11905      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11906      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11908      */
11909     formUpdate : function(form, url, reset, callback){
11910         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11911             if(typeof url == "function"){
11912                 url = url.call(this);
11913             }
11914             form = Roo.getDom(form);
11915             this.transaction = Roo.Ajax.request({
11916                 form: form,
11917                 url:url,
11918                 success: this.successDelegate,
11919                 failure: this.failureDelegate,
11920                 timeout: (this.timeout*1000),
11921                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11922             });
11923             this.showLoading.defer(1, this);
11924         }
11925     },
11926
11927     /**
11928      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11930      */
11931     refresh : function(callback){
11932         if(this.defaultUrl == null){
11933             return;
11934         }
11935         this.update(this.defaultUrl, null, callback, true);
11936     },
11937
11938     /**
11939      * Set this element to auto refresh.
11940      * @param {Number} interval How often to update (in seconds).
11941      * @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)
11942      * @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}
11943      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11944      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11945      */
11946     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11947         if(refreshNow){
11948             this.update(url || this.defaultUrl, params, callback, true);
11949         }
11950         if(this.autoRefreshProcId){
11951             clearInterval(this.autoRefreshProcId);
11952         }
11953         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11954     },
11955
11956     /**
11957      * Stop auto refresh on this element.
11958      */
11959      stopAutoRefresh : function(){
11960         if(this.autoRefreshProcId){
11961             clearInterval(this.autoRefreshProcId);
11962             delete this.autoRefreshProcId;
11963         }
11964     },
11965
11966     isAutoRefreshing : function(){
11967        return this.autoRefreshProcId ? true : false;
11968     },
11969     /**
11970      * Called to update the element to "Loading" state. Override to perform custom action.
11971      */
11972     showLoading : function(){
11973         if(this.showLoadIndicator){
11974             this.el.update(this.indicatorText);
11975         }
11976     },
11977
11978     /**
11979      * Adds unique parameter to query string if disableCaching = true
11980      * @private
11981      */
11982     prepareUrl : function(url){
11983         if(this.disableCaching){
11984             var append = "_dc=" + (new Date().getTime());
11985             if(url.indexOf("?") !== -1){
11986                 url += "&" + append;
11987             }else{
11988                 url += "?" + append;
11989             }
11990         }
11991         return url;
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processSuccess : function(response){
11998         this.transaction = null;
11999         if(response.argument.form && response.argument.reset){
12000             try{ // put in try/catch since some older FF releases had problems with this
12001                 response.argument.form.reset();
12002             }catch(e){}
12003         }
12004         if(this.loadScripts){
12005             this.renderer.render(this.el, response, this,
12006                 this.updateComplete.createDelegate(this, [response]));
12007         }else{
12008             this.renderer.render(this.el, response, this);
12009             this.updateComplete(response);
12010         }
12011     },
12012
12013     updateComplete : function(response){
12014         this.fireEvent("update", this.el, response);
12015         if(typeof response.argument.callback == "function"){
12016             response.argument.callback(this.el, true, response);
12017         }
12018     },
12019
12020     /**
12021      * @private
12022      */
12023     processFailure : function(response){
12024         this.transaction = null;
12025         this.fireEvent("failure", this.el, response);
12026         if(typeof response.argument.callback == "function"){
12027             response.argument.callback(this.el, false, response);
12028         }
12029     },
12030
12031     /**
12032      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12033      * @param {Object} renderer The object implementing the render() method
12034      */
12035     setRenderer : function(renderer){
12036         this.renderer = renderer;
12037     },
12038
12039     getRenderer : function(){
12040        return this.renderer;
12041     },
12042
12043     /**
12044      * Set the defaultUrl used for updates
12045      * @param {String/Function} defaultUrl The url or a function to call to get the url
12046      */
12047     setDefaultUrl : function(defaultUrl){
12048         this.defaultUrl = defaultUrl;
12049     },
12050
12051     /**
12052      * Aborts the executing transaction
12053      */
12054     abort : function(){
12055         if(this.transaction){
12056             Roo.Ajax.abort(this.transaction);
12057         }
12058     },
12059
12060     /**
12061      * Returns true if an update is in progress
12062      * @return {Boolean}
12063      */
12064     isUpdating : function(){
12065         if(this.transaction){
12066             return Roo.Ajax.isLoading(this.transaction);
12067         }
12068         return false;
12069     }
12070 });
12071
12072 /**
12073  * @class Roo.UpdateManager.defaults
12074  * @static (not really - but it helps the doc tool)
12075  * The defaults collection enables customizing the default properties of UpdateManager
12076  */
12077    Roo.UpdateManager.defaults = {
12078        /**
12079          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12080          * @type Number
12081          */
12082          timeout : 30,
12083
12084          /**
12085          * True to process scripts by default (Defaults to false).
12086          * @type Boolean
12087          */
12088         loadScripts : false,
12089
12090         /**
12091         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12092         * @type String
12093         */
12094         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12095         /**
12096          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12097          * @type Boolean
12098          */
12099         disableCaching : false,
12100         /**
12101          * Whether to show indicatorText when loading (Defaults to true).
12102          * @type Boolean
12103          */
12104         showLoadIndicator : true,
12105         /**
12106          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12107          * @type String
12108          */
12109         indicatorText : '<div class="loading-indicator">Loading...</div>'
12110    };
12111
12112 /**
12113  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12114  *Usage:
12115  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12116  * @param {String/HTMLElement/Roo.Element} el The element to update
12117  * @param {String} url The url
12118  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12119  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12120  * @static
12121  * @deprecated
12122  * @member Roo.UpdateManager
12123  */
12124 Roo.UpdateManager.updateElement = function(el, url, params, options){
12125     var um = Roo.get(el, true).getUpdateManager();
12126     Roo.apply(um, options);
12127     um.update(url, params, options ? options.callback : null);
12128 };
12129 // alias for backwards compat
12130 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12131 /**
12132  * @class Roo.UpdateManager.BasicRenderer
12133  * Default Content renderer. Updates the elements innerHTML with the responseText.
12134  */
12135 Roo.UpdateManager.BasicRenderer = function(){};
12136
12137 Roo.UpdateManager.BasicRenderer.prototype = {
12138     /**
12139      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12140      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12141      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12142      * @param {Roo.Element} el The element being rendered
12143      * @param {Object} response The YUI Connect response object
12144      * @param {UpdateManager} updateManager The calling update manager
12145      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12146      */
12147      render : function(el, response, updateManager, callback){
12148         el.update(response.responseText, updateManager.loadScripts, callback);
12149     }
12150 };
12151 /*
12152  * Based on:
12153  * Ext JS Library 1.1.1
12154  * Copyright(c) 2006-2007, Ext JS, LLC.
12155  *
12156  * Originally Released Under LGPL - original licence link has changed is not relivant.
12157  *
12158  * Fork - LGPL
12159  * <script type="text/javascript">
12160  */
12161
12162 /**
12163  * @class Roo.util.DelayedTask
12164  * Provides a convenient method of performing setTimeout where a new
12165  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12166  * You can use this class to buffer
12167  * the keypress events for a certain number of milliseconds, and perform only if they stop
12168  * for that amount of time.
12169  * @constructor The parameters to this constructor serve as defaults and are not required.
12170  * @param {Function} fn (optional) The default function to timeout
12171  * @param {Object} scope (optional) The default scope of that timeout
12172  * @param {Array} args (optional) The default Array of arguments
12173  */
12174 Roo.util.DelayedTask = function(fn, scope, args){
12175     var id = null, d, t;
12176
12177     var call = function(){
12178         var now = new Date().getTime();
12179         if(now - t >= d){
12180             clearInterval(id);
12181             id = null;
12182             fn.apply(scope, args || []);
12183         }
12184     };
12185     /**
12186      * Cancels any pending timeout and queues a new one
12187      * @param {Number} delay The milliseconds to delay
12188      * @param {Function} newFn (optional) Overrides function passed to constructor
12189      * @param {Object} newScope (optional) Overrides scope passed to constructor
12190      * @param {Array} newArgs (optional) Overrides args passed to constructor
12191      */
12192     this.delay = function(delay, newFn, newScope, newArgs){
12193         if(id && delay != d){
12194             this.cancel();
12195         }
12196         d = delay;
12197         t = new Date().getTime();
12198         fn = newFn || fn;
12199         scope = newScope || scope;
12200         args = newArgs || args;
12201         if(!id){
12202             id = setInterval(call, d);
12203         }
12204     };
12205
12206     /**
12207      * Cancel the last queued timeout
12208      */
12209     this.cancel = function(){
12210         if(id){
12211             clearInterval(id);
12212             id = null;
12213         }
12214     };
12215 };/*
12216  * Based on:
12217  * Ext JS Library 1.1.1
12218  * Copyright(c) 2006-2007, Ext JS, LLC.
12219  *
12220  * Originally Released Under LGPL - original licence link has changed is not relivant.
12221  *
12222  * Fork - LGPL
12223  * <script type="text/javascript">
12224  */
12225  
12226  
12227 Roo.util.TaskRunner = function(interval){
12228     interval = interval || 10;
12229     var tasks = [], removeQueue = [];
12230     var id = 0;
12231     var running = false;
12232
12233     var stopThread = function(){
12234         running = false;
12235         clearInterval(id);
12236         id = 0;
12237     };
12238
12239     var startThread = function(){
12240         if(!running){
12241             running = true;
12242             id = setInterval(runTasks, interval);
12243         }
12244     };
12245
12246     var removeTask = function(task){
12247         removeQueue.push(task);
12248         if(task.onStop){
12249             task.onStop();
12250         }
12251     };
12252
12253     var runTasks = function(){
12254         if(removeQueue.length > 0){
12255             for(var i = 0, len = removeQueue.length; i < len; i++){
12256                 tasks.remove(removeQueue[i]);
12257             }
12258             removeQueue = [];
12259             if(tasks.length < 1){
12260                 stopThread();
12261                 return;
12262             }
12263         }
12264         var now = new Date().getTime();
12265         for(var i = 0, len = tasks.length; i < len; ++i){
12266             var t = tasks[i];
12267             var itime = now - t.taskRunTime;
12268             if(t.interval <= itime){
12269                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12270                 t.taskRunTime = now;
12271                 if(rt === false || t.taskRunCount === t.repeat){
12272                     removeTask(t);
12273                     return;
12274                 }
12275             }
12276             if(t.duration && t.duration <= (now - t.taskStartTime)){
12277                 removeTask(t);
12278             }
12279         }
12280     };
12281
12282     /**
12283      * Queues a new task.
12284      * @param {Object} task
12285      */
12286     this.start = function(task){
12287         tasks.push(task);
12288         task.taskStartTime = new Date().getTime();
12289         task.taskRunTime = 0;
12290         task.taskRunCount = 0;
12291         startThread();
12292         return task;
12293     };
12294
12295     this.stop = function(task){
12296         removeTask(task);
12297         return task;
12298     };
12299
12300     this.stopAll = function(){
12301         stopThread();
12302         for(var i = 0, len = tasks.length; i < len; i++){
12303             if(tasks[i].onStop){
12304                 tasks[i].onStop();
12305             }
12306         }
12307         tasks = [];
12308         removeQueue = [];
12309     };
12310 };
12311
12312 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12313  * Based on:
12314  * Ext JS Library 1.1.1
12315  * Copyright(c) 2006-2007, Ext JS, LLC.
12316  *
12317  * Originally Released Under LGPL - original licence link has changed is not relivant.
12318  *
12319  * Fork - LGPL
12320  * <script type="text/javascript">
12321  */
12322
12323  
12324 /**
12325  * @class Roo.util.MixedCollection
12326  * @extends Roo.util.Observable
12327  * A Collection class that maintains both numeric indexes and keys and exposes events.
12328  * @constructor
12329  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12330  * collection (defaults to false)
12331  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12332  * and return the key value for that item.  This is used when available to look up the key on items that
12333  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12334  * equivalent to providing an implementation for the {@link #getKey} method.
12335  */
12336 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12337     this.items = [];
12338     this.map = {};
12339     this.keys = [];
12340     this.length = 0;
12341     this.addEvents({
12342         /**
12343          * @event clear
12344          * Fires when the collection is cleared.
12345          */
12346         "clear" : true,
12347         /**
12348          * @event add
12349          * Fires when an item is added to the collection.
12350          * @param {Number} index The index at which the item was added.
12351          * @param {Object} o The item added.
12352          * @param {String} key The key associated with the added item.
12353          */
12354         "add" : true,
12355         /**
12356          * @event replace
12357          * Fires when an item is replaced in the collection.
12358          * @param {String} key he key associated with the new added.
12359          * @param {Object} old The item being replaced.
12360          * @param {Object} new The new item.
12361          */
12362         "replace" : true,
12363         /**
12364          * @event remove
12365          * Fires when an item is removed from the collection.
12366          * @param {Object} o The item being removed.
12367          * @param {String} key (optional) The key associated with the removed item.
12368          */
12369         "remove" : true,
12370         "sort" : true
12371     });
12372     this.allowFunctions = allowFunctions === true;
12373     if(keyFn){
12374         this.getKey = keyFn;
12375     }
12376     Roo.util.MixedCollection.superclass.constructor.call(this);
12377 };
12378
12379 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12380     allowFunctions : false,
12381     
12382 /**
12383  * Adds an item to the collection.
12384  * @param {String} key The key to associate with the item
12385  * @param {Object} o The item to add.
12386  * @return {Object} The item added.
12387  */
12388     add : function(key, o){
12389         if(arguments.length == 1){
12390             o = arguments[0];
12391             key = this.getKey(o);
12392         }
12393         if(typeof key == "undefined" || key === null){
12394             this.length++;
12395             this.items.push(o);
12396             this.keys.push(null);
12397         }else{
12398             var old = this.map[key];
12399             if(old){
12400                 return this.replace(key, o);
12401             }
12402             this.length++;
12403             this.items.push(o);
12404             this.map[key] = o;
12405             this.keys.push(key);
12406         }
12407         this.fireEvent("add", this.length-1, o, key);
12408         return o;
12409     },
12410        
12411 /**
12412   * MixedCollection has a generic way to fetch keys if you implement getKey.
12413 <pre><code>
12414 // normal way
12415 var mc = new Roo.util.MixedCollection();
12416 mc.add(someEl.dom.id, someEl);
12417 mc.add(otherEl.dom.id, otherEl);
12418 //and so on
12419
12420 // using getKey
12421 var mc = new Roo.util.MixedCollection();
12422 mc.getKey = function(el){
12423    return el.dom.id;
12424 };
12425 mc.add(someEl);
12426 mc.add(otherEl);
12427
12428 // or via the constructor
12429 var mc = new Roo.util.MixedCollection(false, function(el){
12430    return el.dom.id;
12431 });
12432 mc.add(someEl);
12433 mc.add(otherEl);
12434 </code></pre>
12435  * @param o {Object} The item for which to find the key.
12436  * @return {Object} The key for the passed item.
12437  */
12438     getKey : function(o){
12439          return o.id; 
12440     },
12441    
12442 /**
12443  * Replaces an item in the collection.
12444  * @param {String} key The key associated with the item to replace, or the item to replace.
12445  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12446  * @return {Object}  The new item.
12447  */
12448     replace : function(key, o){
12449         if(arguments.length == 1){
12450             o = arguments[0];
12451             key = this.getKey(o);
12452         }
12453         var old = this.item(key);
12454         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12455              return this.add(key, o);
12456         }
12457         var index = this.indexOfKey(key);
12458         this.items[index] = o;
12459         this.map[key] = o;
12460         this.fireEvent("replace", key, old, o);
12461         return o;
12462     },
12463    
12464 /**
12465  * Adds all elements of an Array or an Object to the collection.
12466  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12467  * an Array of values, each of which are added to the collection.
12468  */
12469     addAll : function(objs){
12470         if(arguments.length > 1 || objs instanceof Array){
12471             var args = arguments.length > 1 ? arguments : objs;
12472             for(var i = 0, len = args.length; i < len; i++){
12473                 this.add(args[i]);
12474             }
12475         }else{
12476             for(var key in objs){
12477                 if(this.allowFunctions || typeof objs[key] != "function"){
12478                     this.add(key, objs[key]);
12479                 }
12480             }
12481         }
12482     },
12483    
12484 /**
12485  * Executes the specified function once for every item in the collection, passing each
12486  * item as the first and only parameter. returning false from the function will stop the iteration.
12487  * @param {Function} fn The function to execute for each item.
12488  * @param {Object} scope (optional) The scope in which to execute the function.
12489  */
12490     each : function(fn, scope){
12491         var items = [].concat(this.items); // each safe for removal
12492         for(var i = 0, len = items.length; i < len; i++){
12493             if(fn.call(scope || items[i], items[i], i, len) === false){
12494                 break;
12495             }
12496         }
12497     },
12498    
12499 /**
12500  * Executes the specified function once for every key in the collection, passing each
12501  * key, and its associated item as the first two parameters.
12502  * @param {Function} fn The function to execute for each item.
12503  * @param {Object} scope (optional) The scope in which to execute the function.
12504  */
12505     eachKey : function(fn, scope){
12506         for(var i = 0, len = this.keys.length; i < len; i++){
12507             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12508         }
12509     },
12510    
12511 /**
12512  * Returns the first item in the collection which elicits a true return value from the
12513  * passed selection function.
12514  * @param {Function} fn The selection function to execute for each item.
12515  * @param {Object} scope (optional) The scope in which to execute the function.
12516  * @return {Object} The first item in the collection which returned true from the selection function.
12517  */
12518     find : function(fn, scope){
12519         for(var i = 0, len = this.items.length; i < len; i++){
12520             if(fn.call(scope || window, this.items[i], this.keys[i])){
12521                 return this.items[i];
12522             }
12523         }
12524         return null;
12525     },
12526    
12527 /**
12528  * Inserts an item at the specified index in the collection.
12529  * @param {Number} index The index to insert the item at.
12530  * @param {String} key The key to associate with the new item, or the item itself.
12531  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12532  * @return {Object} The item inserted.
12533  */
12534     insert : function(index, key, o){
12535         if(arguments.length == 2){
12536             o = arguments[1];
12537             key = this.getKey(o);
12538         }
12539         if(index >= this.length){
12540             return this.add(key, o);
12541         }
12542         this.length++;
12543         this.items.splice(index, 0, o);
12544         if(typeof key != "undefined" && key != null){
12545             this.map[key] = o;
12546         }
12547         this.keys.splice(index, 0, key);
12548         this.fireEvent("add", index, o, key);
12549         return o;
12550     },
12551    
12552 /**
12553  * Removed an item from the collection.
12554  * @param {Object} o The item to remove.
12555  * @return {Object} The item removed.
12556  */
12557     remove : function(o){
12558         return this.removeAt(this.indexOf(o));
12559     },
12560    
12561 /**
12562  * Remove an item from a specified index in the collection.
12563  * @param {Number} index The index within the collection of the item to remove.
12564  */
12565     removeAt : function(index){
12566         if(index < this.length && index >= 0){
12567             this.length--;
12568             var o = this.items[index];
12569             this.items.splice(index, 1);
12570             var key = this.keys[index];
12571             if(typeof key != "undefined"){
12572                 delete this.map[key];
12573             }
12574             this.keys.splice(index, 1);
12575             this.fireEvent("remove", o, key);
12576         }
12577     },
12578    
12579 /**
12580  * Removed an item associated with the passed key fom the collection.
12581  * @param {String} key The key of the item to remove.
12582  */
12583     removeKey : function(key){
12584         return this.removeAt(this.indexOfKey(key));
12585     },
12586    
12587 /**
12588  * Returns the number of items in the collection.
12589  * @return {Number} the number of items in the collection.
12590  */
12591     getCount : function(){
12592         return this.length; 
12593     },
12594    
12595 /**
12596  * Returns index within the collection of the passed Object.
12597  * @param {Object} o The item to find the index of.
12598  * @return {Number} index of the item.
12599  */
12600     indexOf : function(o){
12601         if(!this.items.indexOf){
12602             for(var i = 0, len = this.items.length; i < len; i++){
12603                 if(this.items[i] == o) return i;
12604             }
12605             return -1;
12606         }else{
12607             return this.items.indexOf(o);
12608         }
12609     },
12610    
12611 /**
12612  * Returns index within the collection of the passed key.
12613  * @param {String} key The key to find the index of.
12614  * @return {Number} index of the key.
12615  */
12616     indexOfKey : function(key){
12617         if(!this.keys.indexOf){
12618             for(var i = 0, len = this.keys.length; i < len; i++){
12619                 if(this.keys[i] == key) return i;
12620             }
12621             return -1;
12622         }else{
12623             return this.keys.indexOf(key);
12624         }
12625     },
12626    
12627 /**
12628  * Returns the item associated with the passed key OR index. Key has priority over index.
12629  * @param {String/Number} key The key or index of the item.
12630  * @return {Object} The item associated with the passed key.
12631  */
12632     item : function(key){
12633         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12634         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12635     },
12636     
12637 /**
12638  * Returns the item at the specified index.
12639  * @param {Number} index The index of the item.
12640  * @return {Object}
12641  */
12642     itemAt : function(index){
12643         return this.items[index];
12644     },
12645     
12646 /**
12647  * Returns the item associated with the passed key.
12648  * @param {String/Number} key The key of the item.
12649  * @return {Object} The item associated with the passed key.
12650  */
12651     key : function(key){
12652         return this.map[key];
12653     },
12654    
12655 /**
12656  * Returns true if the collection contains the passed Object as an item.
12657  * @param {Object} o  The Object to look for in the collection.
12658  * @return {Boolean} True if the collection contains the Object as an item.
12659  */
12660     contains : function(o){
12661         return this.indexOf(o) != -1;
12662     },
12663    
12664 /**
12665  * Returns true if the collection contains the passed Object as a key.
12666  * @param {String} key The key to look for in the collection.
12667  * @return {Boolean} True if the collection contains the Object as a key.
12668  */
12669     containsKey : function(key){
12670         return typeof this.map[key] != "undefined";
12671     },
12672    
12673 /**
12674  * Removes all items from the collection.
12675  */
12676     clear : function(){
12677         this.length = 0;
12678         this.items = [];
12679         this.keys = [];
12680         this.map = {};
12681         this.fireEvent("clear");
12682     },
12683    
12684 /**
12685  * Returns the first item in the collection.
12686  * @return {Object} the first item in the collection..
12687  */
12688     first : function(){
12689         return this.items[0]; 
12690     },
12691    
12692 /**
12693  * Returns the last item in the collection.
12694  * @return {Object} the last item in the collection..
12695  */
12696     last : function(){
12697         return this.items[this.length-1];   
12698     },
12699     
12700     _sort : function(property, dir, fn){
12701         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12702         fn = fn || function(a, b){
12703             return a-b;
12704         };
12705         var c = [], k = this.keys, items = this.items;
12706         for(var i = 0, len = items.length; i < len; i++){
12707             c[c.length] = {key: k[i], value: items[i], index: i};
12708         }
12709         c.sort(function(a, b){
12710             var v = fn(a[property], b[property]) * dsc;
12711             if(v == 0){
12712                 v = (a.index < b.index ? -1 : 1);
12713             }
12714             return v;
12715         });
12716         for(var i = 0, len = c.length; i < len; i++){
12717             items[i] = c[i].value;
12718             k[i] = c[i].key;
12719         }
12720         this.fireEvent("sort", this);
12721     },
12722     
12723     /**
12724      * Sorts this collection with the passed comparison function
12725      * @param {String} direction (optional) "ASC" or "DESC"
12726      * @param {Function} fn (optional) comparison function
12727      */
12728     sort : function(dir, fn){
12729         this._sort("value", dir, fn);
12730     },
12731     
12732     /**
12733      * Sorts this collection by keys
12734      * @param {String} direction (optional) "ASC" or "DESC"
12735      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12736      */
12737     keySort : function(dir, fn){
12738         this._sort("key", dir, fn || function(a, b){
12739             return String(a).toUpperCase()-String(b).toUpperCase();
12740         });
12741     },
12742     
12743     /**
12744      * Returns a range of items in this collection
12745      * @param {Number} startIndex (optional) defaults to 0
12746      * @param {Number} endIndex (optional) default to the last item
12747      * @return {Array} An array of items
12748      */
12749     getRange : function(start, end){
12750         var items = this.items;
12751         if(items.length < 1){
12752             return [];
12753         }
12754         start = start || 0;
12755         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12756         var r = [];
12757         if(start <= end){
12758             for(var i = start; i <= end; i++) {
12759                     r[r.length] = items[i];
12760             }
12761         }else{
12762             for(var i = start; i >= end; i--) {
12763                     r[r.length] = items[i];
12764             }
12765         }
12766         return r;
12767     },
12768         
12769     /**
12770      * Filter the <i>objects</i> in this collection by a specific property. 
12771      * Returns a new collection that has been filtered.
12772      * @param {String} property A property on your objects
12773      * @param {String/RegExp} value Either string that the property values 
12774      * should start with or a RegExp to test against the property
12775      * @return {MixedCollection} The new filtered collection
12776      */
12777     filter : function(property, value){
12778         if(!value.exec){ // not a regex
12779             value = String(value);
12780             if(value.length == 0){
12781                 return this.clone();
12782             }
12783             value = new RegExp("^" + Roo.escapeRe(value), "i");
12784         }
12785         return this.filterBy(function(o){
12786             return o && value.test(o[property]);
12787         });
12788         },
12789     
12790     /**
12791      * Filter by a function. * Returns a new collection that has been filtered.
12792      * The passed function will be called with each 
12793      * object in the collection. If the function returns true, the value is included 
12794      * otherwise it is filtered.
12795      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12796      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12797      * @return {MixedCollection} The new filtered collection
12798      */
12799     filterBy : function(fn, scope){
12800         var r = new Roo.util.MixedCollection();
12801         r.getKey = this.getKey;
12802         var k = this.keys, it = this.items;
12803         for(var i = 0, len = it.length; i < len; i++){
12804             if(fn.call(scope||this, it[i], k[i])){
12805                                 r.add(k[i], it[i]);
12806                         }
12807         }
12808         return r;
12809     },
12810     
12811     /**
12812      * Creates a duplicate of this collection
12813      * @return {MixedCollection}
12814      */
12815     clone : function(){
12816         var r = new Roo.util.MixedCollection();
12817         var k = this.keys, it = this.items;
12818         for(var i = 0, len = it.length; i < len; i++){
12819             r.add(k[i], it[i]);
12820         }
12821         r.getKey = this.getKey;
12822         return r;
12823     }
12824 });
12825 /**
12826  * Returns the item associated with the passed key or index.
12827  * @method
12828  * @param {String/Number} key The key or index of the item.
12829  * @return {Object} The item associated with the passed key.
12830  */
12831 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12832  * Based on:
12833  * Ext JS Library 1.1.1
12834  * Copyright(c) 2006-2007, Ext JS, LLC.
12835  *
12836  * Originally Released Under LGPL - original licence link has changed is not relivant.
12837  *
12838  * Fork - LGPL
12839  * <script type="text/javascript">
12840  */
12841 /**
12842  * @class Roo.util.JSON
12843  * Modified version of Douglas Crockford"s json.js that doesn"t
12844  * mess with the Object prototype 
12845  * http://www.json.org/js.html
12846  * @singleton
12847  */
12848 Roo.util.JSON = new (function(){
12849     var useHasOwn = {}.hasOwnProperty ? true : false;
12850     
12851     // crashes Safari in some instances
12852     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12853     
12854     var pad = function(n) {
12855         return n < 10 ? "0" + n : n;
12856     };
12857     
12858     var m = {
12859         "\b": '\\b',
12860         "\t": '\\t',
12861         "\n": '\\n',
12862         "\f": '\\f',
12863         "\r": '\\r',
12864         '"' : '\\"',
12865         "\\": '\\\\'
12866     };
12867
12868     var encodeString = function(s){
12869         if (/["\\\x00-\x1f]/.test(s)) {
12870             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12871                 var c = m[b];
12872                 if(c){
12873                     return c;
12874                 }
12875                 c = b.charCodeAt();
12876                 return "\\u00" +
12877                     Math.floor(c / 16).toString(16) +
12878                     (c % 16).toString(16);
12879             }) + '"';
12880         }
12881         return '"' + s + '"';
12882     };
12883     
12884     var encodeArray = function(o){
12885         var a = ["["], b, i, l = o.length, v;
12886             for (i = 0; i < l; i += 1) {
12887                 v = o[i];
12888                 switch (typeof v) {
12889                     case "undefined":
12890                     case "function":
12891                     case "unknown":
12892                         break;
12893                     default:
12894                         if (b) {
12895                             a.push(',');
12896                         }
12897                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12898                         b = true;
12899                 }
12900             }
12901             a.push("]");
12902             return a.join("");
12903     };
12904     
12905     var encodeDate = function(o){
12906         return '"' + o.getFullYear() + "-" +
12907                 pad(o.getMonth() + 1) + "-" +
12908                 pad(o.getDate()) + "T" +
12909                 pad(o.getHours()) + ":" +
12910                 pad(o.getMinutes()) + ":" +
12911                 pad(o.getSeconds()) + '"';
12912     };
12913     
12914     /**
12915      * Encodes an Object, Array or other value
12916      * @param {Mixed} o The variable to encode
12917      * @return {String} The JSON string
12918      */
12919     this.encode = function(o)
12920     {
12921         // should this be extended to fully wrap stringify..
12922         
12923         if(typeof o == "undefined" || o === null){
12924             return "null";
12925         }else if(o instanceof Array){
12926             return encodeArray(o);
12927         }else if(o instanceof Date){
12928             return encodeDate(o);
12929         }else if(typeof o == "string"){
12930             return encodeString(o);
12931         }else if(typeof o == "number"){
12932             return isFinite(o) ? String(o) : "null";
12933         }else if(typeof o == "boolean"){
12934             return String(o);
12935         }else {
12936             var a = ["{"], b, i, v;
12937             for (i in o) {
12938                 if(!useHasOwn || o.hasOwnProperty(i)) {
12939                     v = o[i];
12940                     switch (typeof v) {
12941                     case "undefined":
12942                     case "function":
12943                     case "unknown":
12944                         break;
12945                     default:
12946                         if(b){
12947                             a.push(',');
12948                         }
12949                         a.push(this.encode(i), ":",
12950                                 v === null ? "null" : this.encode(v));
12951                         b = true;
12952                     }
12953                 }
12954             }
12955             a.push("}");
12956             return a.join("");
12957         }
12958     };
12959     
12960     /**
12961      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12962      * @param {String} json The JSON string
12963      * @return {Object} The resulting object
12964      */
12965     this.decode = function(json){
12966         
12967         return  /** eval:var:json */ eval("(" + json + ')');
12968     };
12969 })();
12970 /** 
12971  * Shorthand for {@link Roo.util.JSON#encode}
12972  * @member Roo encode 
12973  * @method */
12974 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12975 /** 
12976  * Shorthand for {@link Roo.util.JSON#decode}
12977  * @member Roo decode 
12978  * @method */
12979 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12980 /*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990  
12991 /**
12992  * @class Roo.util.Format
12993  * Reusable data formatting functions
12994  * @singleton
12995  */
12996 Roo.util.Format = function(){
12997     var trimRe = /^\s+|\s+$/g;
12998     return {
12999         /**
13000          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13001          * @param {String} value The string to truncate
13002          * @param {Number} length The maximum length to allow before truncating
13003          * @return {String} The converted text
13004          */
13005         ellipsis : function(value, len){
13006             if(value && value.length > len){
13007                 return value.substr(0, len-3)+"...";
13008             }
13009             return value;
13010         },
13011
13012         /**
13013          * Checks a reference and converts it to empty string if it is undefined
13014          * @param {Mixed} value Reference to check
13015          * @return {Mixed} Empty string if converted, otherwise the original value
13016          */
13017         undef : function(value){
13018             return typeof value != "undefined" ? value : "";
13019         },
13020
13021         /**
13022          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13023          * @param {String} value The string to encode
13024          * @return {String} The encoded text
13025          */
13026         htmlEncode : function(value){
13027             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13028         },
13029
13030         /**
13031          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13032          * @param {String} value The string to decode
13033          * @return {String} The decoded text
13034          */
13035         htmlDecode : function(value){
13036             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13037         },
13038
13039         /**
13040          * Trims any whitespace from either side of a string
13041          * @param {String} value The text to trim
13042          * @return {String} The trimmed text
13043          */
13044         trim : function(value){
13045             return String(value).replace(trimRe, "");
13046         },
13047
13048         /**
13049          * Returns a substring from within an original string
13050          * @param {String} value The original text
13051          * @param {Number} start The start index of the substring
13052          * @param {Number} length The length of the substring
13053          * @return {String} The substring
13054          */
13055         substr : function(value, start, length){
13056             return String(value).substr(start, length);
13057         },
13058
13059         /**
13060          * Converts a string to all lower case letters
13061          * @param {String} value The text to convert
13062          * @return {String} The converted text
13063          */
13064         lowercase : function(value){
13065             return String(value).toLowerCase();
13066         },
13067
13068         /**
13069          * Converts a string to all upper case letters
13070          * @param {String} value The text to convert
13071          * @return {String} The converted text
13072          */
13073         uppercase : function(value){
13074             return String(value).toUpperCase();
13075         },
13076
13077         /**
13078          * Converts the first character only of a string to upper case
13079          * @param {String} value The text to convert
13080          * @return {String} The converted text
13081          */
13082         capitalize : function(value){
13083             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13084         },
13085
13086         // private
13087         call : function(value, fn){
13088             if(arguments.length > 2){
13089                 var args = Array.prototype.slice.call(arguments, 2);
13090                 args.unshift(value);
13091                  
13092                 return /** eval:var:value */  eval(fn).apply(window, args);
13093             }else{
13094                 /** eval:var:value */
13095                 return /** eval:var:value */ eval(fn).call(window, value);
13096             }
13097         },
13098
13099        
13100         /**
13101          * safer version of Math.toFixed..??/
13102          * @param {Number/String} value The numeric value to format
13103          * @param {Number/String} value Decimal places 
13104          * @return {String} The formatted currency string
13105          */
13106         toFixed : function(v, n)
13107         {
13108             // why not use to fixed - precision is buggered???
13109             if (!n) {
13110                 return Math.round(v-0);
13111             }
13112             var fact = Math.pow(10,n+1);
13113             v = (Math.round((v-0)*fact))/fact;
13114             var z = (''+fact).substring(2);
13115             if (v == Math.floor(v)) {
13116                 return Math.floor(v) + '.' + z;
13117             }
13118             
13119             // now just padd decimals..
13120             var ps = String(v).split('.');
13121             var fd = (ps[1] + z);
13122             var r = fd.substring(0,n); 
13123             var rm = fd.substring(n); 
13124             if (rm < 5) {
13125                 return ps[0] + '.' + r;
13126             }
13127             r*=1; // turn it into a number;
13128             r++;
13129             if (String(r).length != n) {
13130                 ps[0]*=1;
13131                 ps[0]++;
13132                 r = String(r).substring(1); // chop the end off.
13133             }
13134             
13135             return ps[0] + '.' + r;
13136              
13137         },
13138         
13139         /**
13140          * Format a number as US currency
13141          * @param {Number/String} value The numeric value to format
13142          * @return {String} The formatted currency string
13143          */
13144         usMoney : function(v){
13145             v = (Math.round((v-0)*100))/100;
13146             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13147             v = String(v);
13148             var ps = v.split('.');
13149             var whole = ps[0];
13150             var sub = ps[1] ? '.'+ ps[1] : '.00';
13151             var r = /(\d+)(\d{3})/;
13152             while (r.test(whole)) {
13153                 whole = whole.replace(r, '$1' + ',' + '$2');
13154             }
13155             return "$" + whole + sub ;
13156         },
13157         
13158         /**
13159          * Parse a value into a formatted date using the specified format pattern.
13160          * @param {Mixed} value The value to format
13161          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13162          * @return {String} The formatted date string
13163          */
13164         date : function(v, format){
13165             if(!v){
13166                 return "";
13167             }
13168             if(!(v instanceof Date)){
13169                 v = new Date(Date.parse(v));
13170             }
13171             return v.dateFormat(format || "m/d/Y");
13172         },
13173
13174         /**
13175          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13176          * @param {String} format Any valid date format string
13177          * @return {Function} The date formatting function
13178          */
13179         dateRenderer : function(format){
13180             return function(v){
13181                 return Roo.util.Format.date(v, format);  
13182             };
13183         },
13184
13185         // private
13186         stripTagsRE : /<\/?[^>]+>/gi,
13187         
13188         /**
13189          * Strips all HTML tags
13190          * @param {Mixed} value The text from which to strip tags
13191          * @return {String} The stripped text
13192          */
13193         stripTags : function(v){
13194             return !v ? v : String(v).replace(this.stripTagsRE, "");
13195         }
13196     };
13197 }();/*
13198  * Based on:
13199  * Ext JS Library 1.1.1
13200  * Copyright(c) 2006-2007, Ext JS, LLC.
13201  *
13202  * Originally Released Under LGPL - original licence link has changed is not relivant.
13203  *
13204  * Fork - LGPL
13205  * <script type="text/javascript">
13206  */
13207
13208
13209  
13210
13211 /**
13212  * @class Roo.MasterTemplate
13213  * @extends Roo.Template
13214  * Provides a template that can have child templates. The syntax is:
13215 <pre><code>
13216 var t = new Roo.MasterTemplate(
13217         '&lt;select name="{name}"&gt;',
13218                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13219         '&lt;/select&gt;'
13220 );
13221 t.add('options', {value: 'foo', text: 'bar'});
13222 // or you can add multiple child elements in one shot
13223 t.addAll('options', [
13224     {value: 'foo', text: 'bar'},
13225     {value: 'foo2', text: 'bar2'},
13226     {value: 'foo3', text: 'bar3'}
13227 ]);
13228 // then append, applying the master template values
13229 t.append('my-form', {name: 'my-select'});
13230 </code></pre>
13231 * A name attribute for the child template is not required if you have only one child
13232 * template or you want to refer to them by index.
13233  */
13234 Roo.MasterTemplate = function(){
13235     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13236     this.originalHtml = this.html;
13237     var st = {};
13238     var m, re = this.subTemplateRe;
13239     re.lastIndex = 0;
13240     var subIndex = 0;
13241     while(m = re.exec(this.html)){
13242         var name = m[1], content = m[2];
13243         st[subIndex] = {
13244             name: name,
13245             index: subIndex,
13246             buffer: [],
13247             tpl : new Roo.Template(content)
13248         };
13249         if(name){
13250             st[name] = st[subIndex];
13251         }
13252         st[subIndex].tpl.compile();
13253         st[subIndex].tpl.call = this.call.createDelegate(this);
13254         subIndex++;
13255     }
13256     this.subCount = subIndex;
13257     this.subs = st;
13258 };
13259 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13260     /**
13261     * The regular expression used to match sub templates
13262     * @type RegExp
13263     * @property
13264     */
13265     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13266
13267     /**
13268      * Applies the passed values to a child template.
13269      * @param {String/Number} name (optional) The name or index of the child template
13270      * @param {Array/Object} values The values to be applied to the template
13271      * @return {MasterTemplate} this
13272      */
13273      add : function(name, values){
13274         if(arguments.length == 1){
13275             values = arguments[0];
13276             name = 0;
13277         }
13278         var s = this.subs[name];
13279         s.buffer[s.buffer.length] = s.tpl.apply(values);
13280         return this;
13281     },
13282
13283     /**
13284      * Applies all the passed values to a child template.
13285      * @param {String/Number} name (optional) The name or index of the child template
13286      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13287      * @param {Boolean} reset (optional) True to reset the template first
13288      * @return {MasterTemplate} this
13289      */
13290     fill : function(name, values, reset){
13291         var a = arguments;
13292         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13293             values = a[0];
13294             name = 0;
13295             reset = a[1];
13296         }
13297         if(reset){
13298             this.reset();
13299         }
13300         for(var i = 0, len = values.length; i < len; i++){
13301             this.add(name, values[i]);
13302         }
13303         return this;
13304     },
13305
13306     /**
13307      * Resets the template for reuse
13308      * @return {MasterTemplate} this
13309      */
13310      reset : function(){
13311         var s = this.subs;
13312         for(var i = 0; i < this.subCount; i++){
13313             s[i].buffer = [];
13314         }
13315         return this;
13316     },
13317
13318     applyTemplate : function(values){
13319         var s = this.subs;
13320         var replaceIndex = -1;
13321         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13322             return s[++replaceIndex].buffer.join("");
13323         });
13324         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13325     },
13326
13327     apply : function(){
13328         return this.applyTemplate.apply(this, arguments);
13329     },
13330
13331     compile : function(){return this;}
13332 });
13333
13334 /**
13335  * Alias for fill().
13336  * @method
13337  */
13338 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13339  /**
13340  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13341  * var tpl = Roo.MasterTemplate.from('element-id');
13342  * @param {String/HTMLElement} el
13343  * @param {Object} config
13344  * @static
13345  */
13346 Roo.MasterTemplate.from = function(el, config){
13347     el = Roo.getDom(el);
13348     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13349 };/*
13350  * Based on:
13351  * Ext JS Library 1.1.1
13352  * Copyright(c) 2006-2007, Ext JS, LLC.
13353  *
13354  * Originally Released Under LGPL - original licence link has changed is not relivant.
13355  *
13356  * Fork - LGPL
13357  * <script type="text/javascript">
13358  */
13359
13360  
13361 /**
13362  * @class Roo.util.CSS
13363  * Utility class for manipulating CSS rules
13364  * @singleton
13365  */
13366 Roo.util.CSS = function(){
13367         var rules = null;
13368         var doc = document;
13369
13370     var camelRe = /(-[a-z])/gi;
13371     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13372
13373    return {
13374    /**
13375     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13376     * tag and appended to the HEAD of the document.
13377     * @param {String|Object} cssText The text containing the css rules
13378     * @param {String} id An id to add to the stylesheet for later removal
13379     * @return {StyleSheet}
13380     */
13381     createStyleSheet : function(cssText, id){
13382         var ss;
13383         var head = doc.getElementsByTagName("head")[0];
13384         var nrules = doc.createElement("style");
13385         nrules.setAttribute("type", "text/css");
13386         if(id){
13387             nrules.setAttribute("id", id);
13388         }
13389         if (typeof(cssText) != 'string') {
13390             // support object maps..
13391             // not sure if this a good idea.. 
13392             // perhaps it should be merged with the general css handling
13393             // and handle js style props.
13394             var cssTextNew = [];
13395             for(var n in cssText) {
13396                 var citems = [];
13397                 for(var k in cssText[n]) {
13398                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13399                 }
13400                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13401                 
13402             }
13403             cssText = cssTextNew.join("\n");
13404             
13405         }
13406        
13407        
13408        if(Roo.isIE){
13409            head.appendChild(nrules);
13410            ss = nrules.styleSheet;
13411            ss.cssText = cssText;
13412        }else{
13413            try{
13414                 nrules.appendChild(doc.createTextNode(cssText));
13415            }catch(e){
13416                nrules.cssText = cssText; 
13417            }
13418            head.appendChild(nrules);
13419            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13420        }
13421        this.cacheStyleSheet(ss);
13422        return ss;
13423    },
13424
13425    /**
13426     * Removes a style or link tag by id
13427     * @param {String} id The id of the tag
13428     */
13429    removeStyleSheet : function(id){
13430        var existing = doc.getElementById(id);
13431        if(existing){
13432            existing.parentNode.removeChild(existing);
13433        }
13434    },
13435
13436    /**
13437     * Dynamically swaps an existing stylesheet reference for a new one
13438     * @param {String} id The id of an existing link tag to remove
13439     * @param {String} url The href of the new stylesheet to include
13440     */
13441    swapStyleSheet : function(id, url){
13442        this.removeStyleSheet(id);
13443        var ss = doc.createElement("link");
13444        ss.setAttribute("rel", "stylesheet");
13445        ss.setAttribute("type", "text/css");
13446        ss.setAttribute("id", id);
13447        ss.setAttribute("href", url);
13448        doc.getElementsByTagName("head")[0].appendChild(ss);
13449    },
13450    
13451    /**
13452     * Refresh the rule cache if you have dynamically added stylesheets
13453     * @return {Object} An object (hash) of rules indexed by selector
13454     */
13455    refreshCache : function(){
13456        return this.getRules(true);
13457    },
13458
13459    // private
13460    cacheStyleSheet : function(stylesheet){
13461        if(!rules){
13462            rules = {};
13463        }
13464        try{// try catch for cross domain access issue
13465            var ssRules = stylesheet.cssRules || stylesheet.rules;
13466            for(var j = ssRules.length-1; j >= 0; --j){
13467                rules[ssRules[j].selectorText] = ssRules[j];
13468            }
13469        }catch(e){}
13470    },
13471    
13472    /**
13473     * Gets all css rules for the document
13474     * @param {Boolean} refreshCache true to refresh the internal cache
13475     * @return {Object} An object (hash) of rules indexed by selector
13476     */
13477    getRules : function(refreshCache){
13478                 if(rules == null || refreshCache){
13479                         rules = {};
13480                         var ds = doc.styleSheets;
13481                         for(var i =0, len = ds.length; i < len; i++){
13482                             try{
13483                         this.cacheStyleSheet(ds[i]);
13484                     }catch(e){} 
13485                 }
13486                 }
13487                 return rules;
13488         },
13489         
13490         /**
13491     * Gets an an individual CSS rule by selector(s)
13492     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13493     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13494     * @return {CSSRule} The CSS rule or null if one is not found
13495     */
13496    getRule : function(selector, refreshCache){
13497                 var rs = this.getRules(refreshCache);
13498                 if(!(selector instanceof Array)){
13499                     return rs[selector];
13500                 }
13501                 for(var i = 0; i < selector.length; i++){
13502                         if(rs[selector[i]]){
13503                                 return rs[selector[i]];
13504                         }
13505                 }
13506                 return null;
13507         },
13508         
13509         
13510         /**
13511     * Updates a rule property
13512     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13513     * @param {String} property The css property
13514     * @param {String} value The new value for the property
13515     * @return {Boolean} true If a rule was found and updated
13516     */
13517    updateRule : function(selector, property, value){
13518                 if(!(selector instanceof Array)){
13519                         var rule = this.getRule(selector);
13520                         if(rule){
13521                                 rule.style[property.replace(camelRe, camelFn)] = value;
13522                                 return true;
13523                         }
13524                 }else{
13525                         for(var i = 0; i < selector.length; i++){
13526                                 if(this.updateRule(selector[i], property, value)){
13527                                         return true;
13528                                 }
13529                         }
13530                 }
13531                 return false;
13532         }
13533    };   
13534 }();/*
13535  * Based on:
13536  * Ext JS Library 1.1.1
13537  * Copyright(c) 2006-2007, Ext JS, LLC.
13538  *
13539  * Originally Released Under LGPL - original licence link has changed is not relivant.
13540  *
13541  * Fork - LGPL
13542  * <script type="text/javascript">
13543  */
13544
13545  
13546
13547 /**
13548  * @class Roo.util.ClickRepeater
13549  * @extends Roo.util.Observable
13550  * 
13551  * A wrapper class which can be applied to any element. Fires a "click" event while the
13552  * mouse is pressed. The interval between firings may be specified in the config but
13553  * defaults to 10 milliseconds.
13554  * 
13555  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13556  * 
13557  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13558  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13559  * Similar to an autorepeat key delay.
13560  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13561  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13562  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13563  *           "interval" and "delay" are ignored. "immediate" is honored.
13564  * @cfg {Boolean} preventDefault True to prevent the default click event
13565  * @cfg {Boolean} stopDefault True to stop the default click event
13566  * 
13567  * @history
13568  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13569  *     2007-02-02 jvs Renamed to ClickRepeater
13570  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13571  *
13572  *  @constructor
13573  * @param {String/HTMLElement/Element} el The element to listen on
13574  * @param {Object} config
13575  **/
13576 Roo.util.ClickRepeater = function(el, config)
13577 {
13578     this.el = Roo.get(el);
13579     this.el.unselectable();
13580
13581     Roo.apply(this, config);
13582
13583     this.addEvents({
13584     /**
13585      * @event mousedown
13586      * Fires when the mouse button is depressed.
13587      * @param {Roo.util.ClickRepeater} this
13588      */
13589         "mousedown" : true,
13590     /**
13591      * @event click
13592      * Fires on a specified interval during the time the element is pressed.
13593      * @param {Roo.util.ClickRepeater} this
13594      */
13595         "click" : true,
13596     /**
13597      * @event mouseup
13598      * Fires when the mouse key is released.
13599      * @param {Roo.util.ClickRepeater} this
13600      */
13601         "mouseup" : true
13602     });
13603
13604     this.el.on("mousedown", this.handleMouseDown, this);
13605     if(this.preventDefault || this.stopDefault){
13606         this.el.on("click", function(e){
13607             if(this.preventDefault){
13608                 e.preventDefault();
13609             }
13610             if(this.stopDefault){
13611                 e.stopEvent();
13612             }
13613         }, this);
13614     }
13615
13616     // allow inline handler
13617     if(this.handler){
13618         this.on("click", this.handler,  this.scope || this);
13619     }
13620
13621     Roo.util.ClickRepeater.superclass.constructor.call(this);
13622 };
13623
13624 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13625     interval : 20,
13626     delay: 250,
13627     preventDefault : true,
13628     stopDefault : false,
13629     timer : 0,
13630
13631     // private
13632     handleMouseDown : function(){
13633         clearTimeout(this.timer);
13634         this.el.blur();
13635         if(this.pressClass){
13636             this.el.addClass(this.pressClass);
13637         }
13638         this.mousedownTime = new Date();
13639
13640         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13641         this.el.on("mouseout", this.handleMouseOut, this);
13642
13643         this.fireEvent("mousedown", this);
13644         this.fireEvent("click", this);
13645         
13646         this.timer = this.click.defer(this.delay || this.interval, this);
13647     },
13648
13649     // private
13650     click : function(){
13651         this.fireEvent("click", this);
13652         this.timer = this.click.defer(this.getInterval(), this);
13653     },
13654
13655     // private
13656     getInterval: function(){
13657         if(!this.accelerate){
13658             return this.interval;
13659         }
13660         var pressTime = this.mousedownTime.getElapsed();
13661         if(pressTime < 500){
13662             return 400;
13663         }else if(pressTime < 1700){
13664             return 320;
13665         }else if(pressTime < 2600){
13666             return 250;
13667         }else if(pressTime < 3500){
13668             return 180;
13669         }else if(pressTime < 4400){
13670             return 140;
13671         }else if(pressTime < 5300){
13672             return 80;
13673         }else if(pressTime < 6200){
13674             return 50;
13675         }else{
13676             return 10;
13677         }
13678     },
13679
13680     // private
13681     handleMouseOut : function(){
13682         clearTimeout(this.timer);
13683         if(this.pressClass){
13684             this.el.removeClass(this.pressClass);
13685         }
13686         this.el.on("mouseover", this.handleMouseReturn, this);
13687     },
13688
13689     // private
13690     handleMouseReturn : function(){
13691         this.el.un("mouseover", this.handleMouseReturn);
13692         if(this.pressClass){
13693             this.el.addClass(this.pressClass);
13694         }
13695         this.click();
13696     },
13697
13698     // private
13699     handleMouseUp : function(){
13700         clearTimeout(this.timer);
13701         this.el.un("mouseover", this.handleMouseReturn);
13702         this.el.un("mouseout", this.handleMouseOut);
13703         Roo.get(document).un("mouseup", this.handleMouseUp);
13704         this.el.removeClass(this.pressClass);
13705         this.fireEvent("mouseup", this);
13706     }
13707 });/*
13708  * Based on:
13709  * Ext JS Library 1.1.1
13710  * Copyright(c) 2006-2007, Ext JS, LLC.
13711  *
13712  * Originally Released Under LGPL - original licence link has changed is not relivant.
13713  *
13714  * Fork - LGPL
13715  * <script type="text/javascript">
13716  */
13717
13718  
13719 /**
13720  * @class Roo.KeyNav
13721  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13722  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13723  * way to implement custom navigation schemes for any UI component.</p>
13724  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13725  * pageUp, pageDown, del, home, end.  Usage:</p>
13726  <pre><code>
13727 var nav = new Roo.KeyNav("my-element", {
13728     "left" : function(e){
13729         this.moveLeft(e.ctrlKey);
13730     },
13731     "right" : function(e){
13732         this.moveRight(e.ctrlKey);
13733     },
13734     "enter" : function(e){
13735         this.save();
13736     },
13737     scope : this
13738 });
13739 </code></pre>
13740  * @constructor
13741  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13742  * @param {Object} config The config
13743  */
13744 Roo.KeyNav = function(el, config){
13745     this.el = Roo.get(el);
13746     Roo.apply(this, config);
13747     if(!this.disabled){
13748         this.disabled = true;
13749         this.enable();
13750     }
13751 };
13752
13753 Roo.KeyNav.prototype = {
13754     /**
13755      * @cfg {Boolean} disabled
13756      * True to disable this KeyNav instance (defaults to false)
13757      */
13758     disabled : false,
13759     /**
13760      * @cfg {String} defaultEventAction
13761      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13762      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13763      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13764      */
13765     defaultEventAction: "stopEvent",
13766     /**
13767      * @cfg {Boolean} forceKeyDown
13768      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13769      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13770      * handle keydown instead of keypress.
13771      */
13772     forceKeyDown : false,
13773
13774     // private
13775     prepareEvent : function(e){
13776         var k = e.getKey();
13777         var h = this.keyToHandler[k];
13778         //if(h && this[h]){
13779         //    e.stopPropagation();
13780         //}
13781         if(Roo.isSafari && h && k >= 37 && k <= 40){
13782             e.stopEvent();
13783         }
13784     },
13785
13786     // private
13787     relay : function(e){
13788         var k = e.getKey();
13789         var h = this.keyToHandler[k];
13790         if(h && this[h]){
13791             if(this.doRelay(e, this[h], h) !== true){
13792                 e[this.defaultEventAction]();
13793             }
13794         }
13795     },
13796
13797     // private
13798     doRelay : function(e, h, hname){
13799         return h.call(this.scope || this, e);
13800     },
13801
13802     // possible handlers
13803     enter : false,
13804     left : false,
13805     right : false,
13806     up : false,
13807     down : false,
13808     tab : false,
13809     esc : false,
13810     pageUp : false,
13811     pageDown : false,
13812     del : false,
13813     home : false,
13814     end : false,
13815
13816     // quick lookup hash
13817     keyToHandler : {
13818         37 : "left",
13819         39 : "right",
13820         38 : "up",
13821         40 : "down",
13822         33 : "pageUp",
13823         34 : "pageDown",
13824         46 : "del",
13825         36 : "home",
13826         35 : "end",
13827         13 : "enter",
13828         27 : "esc",
13829         9  : "tab"
13830     },
13831
13832         /**
13833          * Enable this KeyNav
13834          */
13835         enable: function(){
13836                 if(this.disabled){
13837             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13838             // the EventObject will normalize Safari automatically
13839             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13840                 this.el.on("keydown", this.relay,  this);
13841             }else{
13842                 this.el.on("keydown", this.prepareEvent,  this);
13843                 this.el.on("keypress", this.relay,  this);
13844             }
13845                     this.disabled = false;
13846                 }
13847         },
13848
13849         /**
13850          * Disable this KeyNav
13851          */
13852         disable: function(){
13853                 if(!this.disabled){
13854                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13855                 this.el.un("keydown", this.relay);
13856             }else{
13857                 this.el.un("keydown", this.prepareEvent);
13858                 this.el.un("keypress", this.relay);
13859             }
13860                     this.disabled = true;
13861                 }
13862         }
13863 };/*
13864  * Based on:
13865  * Ext JS Library 1.1.1
13866  * Copyright(c) 2006-2007, Ext JS, LLC.
13867  *
13868  * Originally Released Under LGPL - original licence link has changed is not relivant.
13869  *
13870  * Fork - LGPL
13871  * <script type="text/javascript">
13872  */
13873
13874  
13875 /**
13876  * @class Roo.KeyMap
13877  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13878  * The constructor accepts the same config object as defined by {@link #addBinding}.
13879  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13880  * combination it will call the function with this signature (if the match is a multi-key
13881  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13882  * A KeyMap can also handle a string representation of keys.<br />
13883  * Usage:
13884  <pre><code>
13885 // map one key by key code
13886 var map = new Roo.KeyMap("my-element", {
13887     key: 13, // or Roo.EventObject.ENTER
13888     fn: myHandler,
13889     scope: myObject
13890 });
13891
13892 // map multiple keys to one action by string
13893 var map = new Roo.KeyMap("my-element", {
13894     key: "a\r\n\t",
13895     fn: myHandler,
13896     scope: myObject
13897 });
13898
13899 // map multiple keys to multiple actions by strings and array of codes
13900 var map = new Roo.KeyMap("my-element", [
13901     {
13902         key: [10,13],
13903         fn: function(){ alert("Return was pressed"); }
13904     }, {
13905         key: "abc",
13906         fn: function(){ alert('a, b or c was pressed'); }
13907     }, {
13908         key: "\t",
13909         ctrl:true,
13910         shift:true,
13911         fn: function(){ alert('Control + shift + tab was pressed.'); }
13912     }
13913 ]);
13914 </code></pre>
13915  * <b>Note: A KeyMap starts enabled</b>
13916  * @constructor
13917  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13918  * @param {Object} config The config (see {@link #addBinding})
13919  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13920  */
13921 Roo.KeyMap = function(el, config, eventName){
13922     this.el  = Roo.get(el);
13923     this.eventName = eventName || "keydown";
13924     this.bindings = [];
13925     if(config){
13926         this.addBinding(config);
13927     }
13928     this.enable();
13929 };
13930
13931 Roo.KeyMap.prototype = {
13932     /**
13933      * True to stop the event from bubbling and prevent the default browser action if the
13934      * key was handled by the KeyMap (defaults to false)
13935      * @type Boolean
13936      */
13937     stopEvent : false,
13938
13939     /**
13940      * Add a new binding to this KeyMap. The following config object properties are supported:
13941      * <pre>
13942 Property    Type             Description
13943 ----------  ---------------  ----------------------------------------------------------------------
13944 key         String/Array     A single keycode or an array of keycodes to handle
13945 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13946 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13947 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13948 fn          Function         The function to call when KeyMap finds the expected key combination
13949 scope       Object           The scope of the callback function
13950 </pre>
13951      *
13952      * Usage:
13953      * <pre><code>
13954 // Create a KeyMap
13955 var map = new Roo.KeyMap(document, {
13956     key: Roo.EventObject.ENTER,
13957     fn: handleKey,
13958     scope: this
13959 });
13960
13961 //Add a new binding to the existing KeyMap later
13962 map.addBinding({
13963     key: 'abc',
13964     shift: true,
13965     fn: handleKey,
13966     scope: this
13967 });
13968 </code></pre>
13969      * @param {Object/Array} config A single KeyMap config or an array of configs
13970      */
13971         addBinding : function(config){
13972         if(config instanceof Array){
13973             for(var i = 0, len = config.length; i < len; i++){
13974                 this.addBinding(config[i]);
13975             }
13976             return;
13977         }
13978         var keyCode = config.key,
13979             shift = config.shift, 
13980             ctrl = config.ctrl, 
13981             alt = config.alt,
13982             fn = config.fn,
13983             scope = config.scope;
13984         if(typeof keyCode == "string"){
13985             var ks = [];
13986             var keyString = keyCode.toUpperCase();
13987             for(var j = 0, len = keyString.length; j < len; j++){
13988                 ks.push(keyString.charCodeAt(j));
13989             }
13990             keyCode = ks;
13991         }
13992         var keyArray = keyCode instanceof Array;
13993         var handler = function(e){
13994             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13995                 var k = e.getKey();
13996                 if(keyArray){
13997                     for(var i = 0, len = keyCode.length; i < len; i++){
13998                         if(keyCode[i] == k){
13999                           if(this.stopEvent){
14000                               e.stopEvent();
14001                           }
14002                           fn.call(scope || window, k, e);
14003                           return;
14004                         }
14005                     }
14006                 }else{
14007                     if(k == keyCode){
14008                         if(this.stopEvent){
14009                            e.stopEvent();
14010                         }
14011                         fn.call(scope || window, k, e);
14012                     }
14013                 }
14014             }
14015         };
14016         this.bindings.push(handler);  
14017         },
14018
14019     /**
14020      * Shorthand for adding a single key listener
14021      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14022      * following options:
14023      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14024      * @param {Function} fn The function to call
14025      * @param {Object} scope (optional) The scope of the function
14026      */
14027     on : function(key, fn, scope){
14028         var keyCode, shift, ctrl, alt;
14029         if(typeof key == "object" && !(key instanceof Array)){
14030             keyCode = key.key;
14031             shift = key.shift;
14032             ctrl = key.ctrl;
14033             alt = key.alt;
14034         }else{
14035             keyCode = key;
14036         }
14037         this.addBinding({
14038             key: keyCode,
14039             shift: shift,
14040             ctrl: ctrl,
14041             alt: alt,
14042             fn: fn,
14043             scope: scope
14044         })
14045     },
14046
14047     // private
14048     handleKeyDown : function(e){
14049             if(this.enabled){ //just in case
14050             var b = this.bindings;
14051             for(var i = 0, len = b.length; i < len; i++){
14052                 b[i].call(this, e);
14053             }
14054             }
14055         },
14056         
14057         /**
14058          * Returns true if this KeyMap is enabled
14059          * @return {Boolean} 
14060          */
14061         isEnabled : function(){
14062             return this.enabled;  
14063         },
14064         
14065         /**
14066          * Enables this KeyMap
14067          */
14068         enable: function(){
14069                 if(!this.enabled){
14070                     this.el.on(this.eventName, this.handleKeyDown, this);
14071                     this.enabled = true;
14072                 }
14073         },
14074
14075         /**
14076          * Disable this KeyMap
14077          */
14078         disable: function(){
14079                 if(this.enabled){
14080                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14081                     this.enabled = false;
14082                 }
14083         }
14084 };/*
14085  * Based on:
14086  * Ext JS Library 1.1.1
14087  * Copyright(c) 2006-2007, Ext JS, LLC.
14088  *
14089  * Originally Released Under LGPL - original licence link has changed is not relivant.
14090  *
14091  * Fork - LGPL
14092  * <script type="text/javascript">
14093  */
14094
14095  
14096 /**
14097  * @class Roo.util.TextMetrics
14098  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14099  * wide, in pixels, a given block of text will be.
14100  * @singleton
14101  */
14102 Roo.util.TextMetrics = function(){
14103     var shared;
14104     return {
14105         /**
14106          * Measures the size of the specified text
14107          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14108          * that can affect the size of the rendered text
14109          * @param {String} text The text to measure
14110          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14111          * in order to accurately measure the text height
14112          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14113          */
14114         measure : function(el, text, fixedWidth){
14115             if(!shared){
14116                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14117             }
14118             shared.bind(el);
14119             shared.setFixedWidth(fixedWidth || 'auto');
14120             return shared.getSize(text);
14121         },
14122
14123         /**
14124          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14125          * the overhead of multiple calls to initialize the style properties on each measurement.
14126          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14127          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14128          * in order to accurately measure the text height
14129          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14130          */
14131         createInstance : function(el, fixedWidth){
14132             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14133         }
14134     };
14135 }();
14136
14137  
14138
14139 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14140     var ml = new Roo.Element(document.createElement('div'));
14141     document.body.appendChild(ml.dom);
14142     ml.position('absolute');
14143     ml.setLeftTop(-1000, -1000);
14144     ml.hide();
14145
14146     if(fixedWidth){
14147         ml.setWidth(fixedWidth);
14148     }
14149      
14150     var instance = {
14151         /**
14152          * Returns the size of the specified text based on the internal element's style and width properties
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {String} text The text to measure
14155          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14156          */
14157         getSize : function(text){
14158             ml.update(text);
14159             var s = ml.getSize();
14160             ml.update('');
14161             return s;
14162         },
14163
14164         /**
14165          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14166          * that can affect the size of the rendered text
14167          * @memberOf Roo.util.TextMetrics.Instance#
14168          * @param {String/HTMLElement} el The element, dom node or id
14169          */
14170         bind : function(el){
14171             ml.setStyle(
14172                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14173             );
14174         },
14175
14176         /**
14177          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14178          * to set a fixed width in order to accurately measure the text height.
14179          * @memberOf Roo.util.TextMetrics.Instance#
14180          * @param {Number} width The width to set on the element
14181          */
14182         setFixedWidth : function(width){
14183             ml.setWidth(width);
14184         },
14185
14186         /**
14187          * Returns the measured width of the specified text
14188          * @memberOf Roo.util.TextMetrics.Instance#
14189          * @param {String} text The text to measure
14190          * @return {Number} width The width in pixels
14191          */
14192         getWidth : function(text){
14193             ml.dom.style.width = 'auto';
14194             return this.getSize(text).width;
14195         },
14196
14197         /**
14198          * Returns the measured height of the specified text.  For multiline text, be sure to call
14199          * {@link #setFixedWidth} if necessary.
14200          * @memberOf Roo.util.TextMetrics.Instance#
14201          * @param {String} text The text to measure
14202          * @return {Number} height The height in pixels
14203          */
14204         getHeight : function(text){
14205             return this.getSize(text).height;
14206         }
14207     };
14208
14209     instance.bind(bindTo);
14210
14211     return instance;
14212 };
14213
14214 // backwards compat
14215 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14216  * Based on:
14217  * Ext JS Library 1.1.1
14218  * Copyright(c) 2006-2007, Ext JS, LLC.
14219  *
14220  * Originally Released Under LGPL - original licence link has changed is not relivant.
14221  *
14222  * Fork - LGPL
14223  * <script type="text/javascript">
14224  */
14225
14226 /**
14227  * @class Roo.state.Provider
14228  * Abstract base class for state provider implementations. This class provides methods
14229  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14230  * Provider interface.
14231  */
14232 Roo.state.Provider = function(){
14233     /**
14234      * @event statechange
14235      * Fires when a state change occurs.
14236      * @param {Provider} this This state provider
14237      * @param {String} key The state key which was changed
14238      * @param {String} value The encoded value for the state
14239      */
14240     this.addEvents({
14241         "statechange": true
14242     });
14243     this.state = {};
14244     Roo.state.Provider.superclass.constructor.call(this);
14245 };
14246 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14247     /**
14248      * Returns the current value for a key
14249      * @param {String} name The key name
14250      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14251      * @return {Mixed} The state data
14252      */
14253     get : function(name, defaultValue){
14254         return typeof this.state[name] == "undefined" ?
14255             defaultValue : this.state[name];
14256     },
14257     
14258     /**
14259      * Clears a value from the state
14260      * @param {String} name The key name
14261      */
14262     clear : function(name){
14263         delete this.state[name];
14264         this.fireEvent("statechange", this, name, null);
14265     },
14266     
14267     /**
14268      * Sets the value for a key
14269      * @param {String} name The key name
14270      * @param {Mixed} value The value to set
14271      */
14272     set : function(name, value){
14273         this.state[name] = value;
14274         this.fireEvent("statechange", this, name, value);
14275     },
14276     
14277     /**
14278      * Decodes a string previously encoded with {@link #encodeValue}.
14279      * @param {String} value The value to decode
14280      * @return {Mixed} The decoded value
14281      */
14282     decodeValue : function(cookie){
14283         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14284         var matches = re.exec(unescape(cookie));
14285         if(!matches || !matches[1]) return; // non state cookie
14286         var type = matches[1];
14287         var v = matches[2];
14288         switch(type){
14289             case "n":
14290                 return parseFloat(v);
14291             case "d":
14292                 return new Date(Date.parse(v));
14293             case "b":
14294                 return (v == "1");
14295             case "a":
14296                 var all = [];
14297                 var values = v.split("^");
14298                 for(var i = 0, len = values.length; i < len; i++){
14299                     all.push(this.decodeValue(values[i]));
14300                 }
14301                 return all;
14302            case "o":
14303                 var all = {};
14304                 var values = v.split("^");
14305                 for(var i = 0, len = values.length; i < len; i++){
14306                     var kv = values[i].split("=");
14307                     all[kv[0]] = this.decodeValue(kv[1]);
14308                 }
14309                 return all;
14310            default:
14311                 return v;
14312         }
14313     },
14314     
14315     /**
14316      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14317      * @param {Mixed} value The value to encode
14318      * @return {String} The encoded value
14319      */
14320     encodeValue : function(v){
14321         var enc;
14322         if(typeof v == "number"){
14323             enc = "n:" + v;
14324         }else if(typeof v == "boolean"){
14325             enc = "b:" + (v ? "1" : "0");
14326         }else if(v instanceof Date){
14327             enc = "d:" + v.toGMTString();
14328         }else if(v instanceof Array){
14329             var flat = "";
14330             for(var i = 0, len = v.length; i < len; i++){
14331                 flat += this.encodeValue(v[i]);
14332                 if(i != len-1) flat += "^";
14333             }
14334             enc = "a:" + flat;
14335         }else if(typeof v == "object"){
14336             var flat = "";
14337             for(var key in v){
14338                 if(typeof v[key] != "function"){
14339                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14340                 }
14341             }
14342             enc = "o:" + flat.substring(0, flat.length-1);
14343         }else{
14344             enc = "s:" + v;
14345         }
14346         return escape(enc);        
14347     }
14348 });
14349
14350 /*
14351  * Based on:
14352  * Ext JS Library 1.1.1
14353  * Copyright(c) 2006-2007, Ext JS, LLC.
14354  *
14355  * Originally Released Under LGPL - original licence link has changed is not relivant.
14356  *
14357  * Fork - LGPL
14358  * <script type="text/javascript">
14359  */
14360 /**
14361  * @class Roo.state.Manager
14362  * This is the global state manager. By default all components that are "state aware" check this class
14363  * for state information if you don't pass them a custom state provider. In order for this class
14364  * to be useful, it must be initialized with a provider when your application initializes.
14365  <pre><code>
14366 // in your initialization function
14367 init : function(){
14368    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14369    ...
14370    // supposed you have a {@link Roo.BorderLayout}
14371    var layout = new Roo.BorderLayout(...);
14372    layout.restoreState();
14373    // or a {Roo.BasicDialog}
14374    var dialog = new Roo.BasicDialog(...);
14375    dialog.restoreState();
14376  </code></pre>
14377  * @singleton
14378  */
14379 Roo.state.Manager = function(){
14380     var provider = new Roo.state.Provider();
14381     
14382     return {
14383         /**
14384          * Configures the default state provider for your application
14385          * @param {Provider} stateProvider The state provider to set
14386          */
14387         setProvider : function(stateProvider){
14388             provider = stateProvider;
14389         },
14390         
14391         /**
14392          * Returns the current value for a key
14393          * @param {String} name The key name
14394          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14395          * @return {Mixed} The state data
14396          */
14397         get : function(key, defaultValue){
14398             return provider.get(key, defaultValue);
14399         },
14400         
14401         /**
14402          * Sets the value for a key
14403          * @param {String} name The key name
14404          * @param {Mixed} value The state data
14405          */
14406          set : function(key, value){
14407             provider.set(key, value);
14408         },
14409         
14410         /**
14411          * Clears a value from the state
14412          * @param {String} name The key name
14413          */
14414         clear : function(key){
14415             provider.clear(key);
14416         },
14417         
14418         /**
14419          * Gets the currently configured state provider
14420          * @return {Provider} The state provider
14421          */
14422         getProvider : function(){
14423             return provider;
14424         }
14425     };
14426 }();
14427 /*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437 /**
14438  * @class Roo.state.CookieProvider
14439  * @extends Roo.state.Provider
14440  * The default Provider implementation which saves state via cookies.
14441  * <br />Usage:
14442  <pre><code>
14443    var cp = new Roo.state.CookieProvider({
14444        path: "/cgi-bin/",
14445        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14446        domain: "roojs.com"
14447    })
14448    Roo.state.Manager.setProvider(cp);
14449  </code></pre>
14450  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14451  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14452  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14453  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14454  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14455  * domain the page is running on including the 'www' like 'www.roojs.com')
14456  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14457  * @constructor
14458  * Create a new CookieProvider
14459  * @param {Object} config The configuration object
14460  */
14461 Roo.state.CookieProvider = function(config){
14462     Roo.state.CookieProvider.superclass.constructor.call(this);
14463     this.path = "/";
14464     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14465     this.domain = null;
14466     this.secure = false;
14467     Roo.apply(this, config);
14468     this.state = this.readCookies();
14469 };
14470
14471 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14472     // private
14473     set : function(name, value){
14474         if(typeof value == "undefined" || value === null){
14475             this.clear(name);
14476             return;
14477         }
14478         this.setCookie(name, value);
14479         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14480     },
14481
14482     // private
14483     clear : function(name){
14484         this.clearCookie(name);
14485         Roo.state.CookieProvider.superclass.clear.call(this, name);
14486     },
14487
14488     // private
14489     readCookies : function(){
14490         var cookies = {};
14491         var c = document.cookie + ";";
14492         var re = /\s?(.*?)=(.*?);/g;
14493         var matches;
14494         while((matches = re.exec(c)) != null){
14495             var name = matches[1];
14496             var value = matches[2];
14497             if(name && name.substring(0,3) == "ys-"){
14498                 cookies[name.substr(3)] = this.decodeValue(value);
14499             }
14500         }
14501         return cookies;
14502     },
14503
14504     // private
14505     setCookie : function(name, value){
14506         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14507            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14508            ((this.path == null) ? "" : ("; path=" + this.path)) +
14509            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14510            ((this.secure == true) ? "; secure" : "");
14511     },
14512
14513     // private
14514     clearCookie : function(name){
14515         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14516            ((this.path == null) ? "" : ("; path=" + this.path)) +
14517            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14518            ((this.secure == true) ? "; secure" : "");
14519     }
14520 });/*
14521  * Based on:
14522  * Ext JS Library 1.1.1
14523  * Copyright(c) 2006-2007, Ext JS, LLC.
14524  *
14525  * Originally Released Under LGPL - original licence link has changed is not relivant.
14526  *
14527  * Fork - LGPL
14528  * <script type="text/javascript">
14529  */
14530
14531
14532
14533 /*
14534  * These classes are derivatives of the similarly named classes in the YUI Library.
14535  * The original license:
14536  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14537  * Code licensed under the BSD License:
14538  * http://developer.yahoo.net/yui/license.txt
14539  */
14540
14541 (function() {
14542
14543 var Event=Roo.EventManager;
14544 var Dom=Roo.lib.Dom;
14545
14546 /**
14547  * @class Roo.dd.DragDrop
14548  * @extends Roo.util.Observable
14549  * Defines the interface and base operation of items that that can be
14550  * dragged or can be drop targets.  It was designed to be extended, overriding
14551  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14552  * Up to three html elements can be associated with a DragDrop instance:
14553  * <ul>
14554  * <li>linked element: the element that is passed into the constructor.
14555  * This is the element which defines the boundaries for interaction with
14556  * other DragDrop objects.</li>
14557  * <li>handle element(s): The drag operation only occurs if the element that
14558  * was clicked matches a handle element.  By default this is the linked
14559  * element, but there are times that you will want only a portion of the
14560  * linked element to initiate the drag operation, and the setHandleElId()
14561  * method provides a way to define this.</li>
14562  * <li>drag element: this represents the element that would be moved along
14563  * with the cursor during a drag operation.  By default, this is the linked
14564  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14565  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14566  * </li>
14567  * </ul>
14568  * This class should not be instantiated until the onload event to ensure that
14569  * the associated elements are available.
14570  * The following would define a DragDrop obj that would interact with any
14571  * other DragDrop obj in the "group1" group:
14572  * <pre>
14573  *  dd = new Roo.dd.DragDrop("div1", "group1");
14574  * </pre>
14575  * Since none of the event handlers have been implemented, nothing would
14576  * actually happen if you were to run the code above.  Normally you would
14577  * override this class or one of the default implementations, but you can
14578  * also override the methods you want on an instance of the class...
14579  * <pre>
14580  *  dd.onDragDrop = function(e, id) {
14581  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14582  *  }
14583  * </pre>
14584  * @constructor
14585  * @param {String} id of the element that is linked to this instance
14586  * @param {String} sGroup the group of related DragDrop objects
14587  * @param {object} config an object containing configurable attributes
14588  *                Valid properties for DragDrop:
14589  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14590  */
14591 Roo.dd.DragDrop = function(id, sGroup, config) {
14592     if (id) {
14593         this.init(id, sGroup, config);
14594     }
14595     
14596 };
14597
14598 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14599
14600     /**
14601      * The id of the element associated with this object.  This is what we
14602      * refer to as the "linked element" because the size and position of
14603      * this element is used to determine when the drag and drop objects have
14604      * interacted.
14605      * @property id
14606      * @type String
14607      */
14608     id: null,
14609
14610     /**
14611      * Configuration attributes passed into the constructor
14612      * @property config
14613      * @type object
14614      */
14615     config: null,
14616
14617     /**
14618      * The id of the element that will be dragged.  By default this is same
14619      * as the linked element , but could be changed to another element. Ex:
14620      * Roo.dd.DDProxy
14621      * @property dragElId
14622      * @type String
14623      * @private
14624      */
14625     dragElId: null,
14626
14627     /**
14628      * the id of the element that initiates the drag operation.  By default
14629      * this is the linked element, but could be changed to be a child of this
14630      * element.  This lets us do things like only starting the drag when the
14631      * header element within the linked html element is clicked.
14632      * @property handleElId
14633      * @type String
14634      * @private
14635      */
14636     handleElId: null,
14637
14638     /**
14639      * An associative array of HTML tags that will be ignored if clicked.
14640      * @property invalidHandleTypes
14641      * @type {string: string}
14642      */
14643     invalidHandleTypes: null,
14644
14645     /**
14646      * An associative array of ids for elements that will be ignored if clicked
14647      * @property invalidHandleIds
14648      * @type {string: string}
14649      */
14650     invalidHandleIds: null,
14651
14652     /**
14653      * An indexted array of css class names for elements that will be ignored
14654      * if clicked.
14655      * @property invalidHandleClasses
14656      * @type string[]
14657      */
14658     invalidHandleClasses: null,
14659
14660     /**
14661      * The linked element's absolute X position at the time the drag was
14662      * started
14663      * @property startPageX
14664      * @type int
14665      * @private
14666      */
14667     startPageX: 0,
14668
14669     /**
14670      * The linked element's absolute X position at the time the drag was
14671      * started
14672      * @property startPageY
14673      * @type int
14674      * @private
14675      */
14676     startPageY: 0,
14677
14678     /**
14679      * The group defines a logical collection of DragDrop objects that are
14680      * related.  Instances only get events when interacting with other
14681      * DragDrop object in the same group.  This lets us define multiple
14682      * groups using a single DragDrop subclass if we want.
14683      * @property groups
14684      * @type {string: string}
14685      */
14686     groups: null,
14687
14688     /**
14689      * Individual drag/drop instances can be locked.  This will prevent
14690      * onmousedown start drag.
14691      * @property locked
14692      * @type boolean
14693      * @private
14694      */
14695     locked: false,
14696
14697     /**
14698      * Lock this instance
14699      * @method lock
14700      */
14701     lock: function() { this.locked = true; },
14702
14703     /**
14704      * Unlock this instace
14705      * @method unlock
14706      */
14707     unlock: function() { this.locked = false; },
14708
14709     /**
14710      * By default, all insances can be a drop target.  This can be disabled by
14711      * setting isTarget to false.
14712      * @method isTarget
14713      * @type boolean
14714      */
14715     isTarget: true,
14716
14717     /**
14718      * The padding configured for this drag and drop object for calculating
14719      * the drop zone intersection with this object.
14720      * @method padding
14721      * @type int[]
14722      */
14723     padding: null,
14724
14725     /**
14726      * Cached reference to the linked element
14727      * @property _domRef
14728      * @private
14729      */
14730     _domRef: null,
14731
14732     /**
14733      * Internal typeof flag
14734      * @property __ygDragDrop
14735      * @private
14736      */
14737     __ygDragDrop: true,
14738
14739     /**
14740      * Set to true when horizontal contraints are applied
14741      * @property constrainX
14742      * @type boolean
14743      * @private
14744      */
14745     constrainX: false,
14746
14747     /**
14748      * Set to true when vertical contraints are applied
14749      * @property constrainY
14750      * @type boolean
14751      * @private
14752      */
14753     constrainY: false,
14754
14755     /**
14756      * The left constraint
14757      * @property minX
14758      * @type int
14759      * @private
14760      */
14761     minX: 0,
14762
14763     /**
14764      * The right constraint
14765      * @property maxX
14766      * @type int
14767      * @private
14768      */
14769     maxX: 0,
14770
14771     /**
14772      * The up constraint
14773      * @property minY
14774      * @type int
14775      * @type int
14776      * @private
14777      */
14778     minY: 0,
14779
14780     /**
14781      * The down constraint
14782      * @property maxY
14783      * @type int
14784      * @private
14785      */
14786     maxY: 0,
14787
14788     /**
14789      * Maintain offsets when we resetconstraints.  Set to true when you want
14790      * the position of the element relative to its parent to stay the same
14791      * when the page changes
14792      *
14793      * @property maintainOffset
14794      * @type boolean
14795      */
14796     maintainOffset: false,
14797
14798     /**
14799      * Array of pixel locations the element will snap to if we specified a
14800      * horizontal graduation/interval.  This array is generated automatically
14801      * when you define a tick interval.
14802      * @property xTicks
14803      * @type int[]
14804      */
14805     xTicks: null,
14806
14807     /**
14808      * Array of pixel locations the element will snap to if we specified a
14809      * vertical graduation/interval.  This array is generated automatically
14810      * when you define a tick interval.
14811      * @property yTicks
14812      * @type int[]
14813      */
14814     yTicks: null,
14815
14816     /**
14817      * By default the drag and drop instance will only respond to the primary
14818      * button click (left button for a right-handed mouse).  Set to true to
14819      * allow drag and drop to start with any mouse click that is propogated
14820      * by the browser
14821      * @property primaryButtonOnly
14822      * @type boolean
14823      */
14824     primaryButtonOnly: true,
14825
14826     /**
14827      * The availabe property is false until the linked dom element is accessible.
14828      * @property available
14829      * @type boolean
14830      */
14831     available: false,
14832
14833     /**
14834      * By default, drags can only be initiated if the mousedown occurs in the
14835      * region the linked element is.  This is done in part to work around a
14836      * bug in some browsers that mis-report the mousedown if the previous
14837      * mouseup happened outside of the window.  This property is set to true
14838      * if outer handles are defined.
14839      *
14840      * @property hasOuterHandles
14841      * @type boolean
14842      * @default false
14843      */
14844     hasOuterHandles: false,
14845
14846     /**
14847      * Code that executes immediately before the startDrag event
14848      * @method b4StartDrag
14849      * @private
14850      */
14851     b4StartDrag: function(x, y) { },
14852
14853     /**
14854      * Abstract method called after a drag/drop object is clicked
14855      * and the drag or mousedown time thresholds have beeen met.
14856      * @method startDrag
14857      * @param {int} X click location
14858      * @param {int} Y click location
14859      */
14860     startDrag: function(x, y) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDrag event
14864      * @method b4Drag
14865      * @private
14866      */
14867     b4Drag: function(e) { },
14868
14869     /**
14870      * Abstract method called during the onMouseMove event while dragging an
14871      * object.
14872      * @method onDrag
14873      * @param {Event} e the mousemove event
14874      */
14875     onDrag: function(e) { /* override this */ },
14876
14877     /**
14878      * Abstract method called when this element fist begins hovering over
14879      * another DragDrop obj
14880      * @method onDragEnter
14881      * @param {Event} e the mousemove event
14882      * @param {String|DragDrop[]} id In POINT mode, the element
14883      * id this is hovering over.  In INTERSECT mode, an array of one or more
14884      * dragdrop items being hovered over.
14885      */
14886     onDragEnter: function(e, id) { /* override this */ },
14887
14888     /**
14889      * Code that executes immediately before the onDragOver event
14890      * @method b4DragOver
14891      * @private
14892      */
14893     b4DragOver: function(e) { },
14894
14895     /**
14896      * Abstract method called when this element is hovering over another
14897      * DragDrop obj
14898      * @method onDragOver
14899      * @param {Event} e the mousemove event
14900      * @param {String|DragDrop[]} id In POINT mode, the element
14901      * id this is hovering over.  In INTERSECT mode, an array of dd items
14902      * being hovered over.
14903      */
14904     onDragOver: function(e, id) { /* override this */ },
14905
14906     /**
14907      * Code that executes immediately before the onDragOut event
14908      * @method b4DragOut
14909      * @private
14910      */
14911     b4DragOut: function(e) { },
14912
14913     /**
14914      * Abstract method called when we are no longer hovering over an element
14915      * @method onDragOut
14916      * @param {Event} e the mousemove event
14917      * @param {String|DragDrop[]} id In POINT mode, the element
14918      * id this was hovering over.  In INTERSECT mode, an array of dd items
14919      * that the mouse is no longer over.
14920      */
14921     onDragOut: function(e, id) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the onDragDrop event
14925      * @method b4DragDrop
14926      * @private
14927      */
14928     b4DragDrop: function(e) { },
14929
14930     /**
14931      * Abstract method called when this item is dropped on another DragDrop
14932      * obj
14933      * @method onDragDrop
14934      * @param {Event} e the mouseup event
14935      * @param {String|DragDrop[]} id In POINT mode, the element
14936      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14937      * was dropped on.
14938      */
14939     onDragDrop: function(e, id) { /* override this */ },
14940
14941     /**
14942      * Abstract method called when this item is dropped on an area with no
14943      * drop target
14944      * @method onInvalidDrop
14945      * @param {Event} e the mouseup event
14946      */
14947     onInvalidDrop: function(e) { /* override this */ },
14948
14949     /**
14950      * Code that executes immediately before the endDrag event
14951      * @method b4EndDrag
14952      * @private
14953      */
14954     b4EndDrag: function(e) { },
14955
14956     /**
14957      * Fired when we are done dragging the object
14958      * @method endDrag
14959      * @param {Event} e the mouseup event
14960      */
14961     endDrag: function(e) { /* override this */ },
14962
14963     /**
14964      * Code executed immediately before the onMouseDown event
14965      * @method b4MouseDown
14966      * @param {Event} e the mousedown event
14967      * @private
14968      */
14969     b4MouseDown: function(e) {  },
14970
14971     /**
14972      * Event handler that fires when a drag/drop obj gets a mousedown
14973      * @method onMouseDown
14974      * @param {Event} e the mousedown event
14975      */
14976     onMouseDown: function(e) { /* override this */ },
14977
14978     /**
14979      * Event handler that fires when a drag/drop obj gets a mouseup
14980      * @method onMouseUp
14981      * @param {Event} e the mouseup event
14982      */
14983     onMouseUp: function(e) { /* override this */ },
14984
14985     /**
14986      * Override the onAvailable method to do what is needed after the initial
14987      * position was determined.
14988      * @method onAvailable
14989      */
14990     onAvailable: function () {
14991     },
14992
14993     /*
14994      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14995      * @type Object
14996      */
14997     defaultPadding : {left:0, right:0, top:0, bottom:0},
14998
14999     /*
15000      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15001  *
15002  * Usage:
15003  <pre><code>
15004  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15005                 { dragElId: "existingProxyDiv" });
15006  dd.startDrag = function(){
15007      this.constrainTo("parent-id");
15008  };
15009  </code></pre>
15010  * Or you can initalize it using the {@link Roo.Element} object:
15011  <pre><code>
15012  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15013      startDrag : function(){
15014          this.constrainTo("parent-id");
15015      }
15016  });
15017  </code></pre>
15018      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15019      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15020      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15021      * an object containing the sides to pad. For example: {right:10, bottom:10}
15022      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15023      */
15024     constrainTo : function(constrainTo, pad, inContent){
15025         if(typeof pad == "number"){
15026             pad = {left: pad, right:pad, top:pad, bottom:pad};
15027         }
15028         pad = pad || this.defaultPadding;
15029         var b = Roo.get(this.getEl()).getBox();
15030         var ce = Roo.get(constrainTo);
15031         var s = ce.getScroll();
15032         var c, cd = ce.dom;
15033         if(cd == document.body){
15034             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15035         }else{
15036             xy = ce.getXY();
15037             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15038         }
15039
15040
15041         var topSpace = b.y - c.y;
15042         var leftSpace = b.x - c.x;
15043
15044         this.resetConstraints();
15045         this.setXConstraint(leftSpace - (pad.left||0), // left
15046                 c.width - leftSpace - b.width - (pad.right||0) //right
15047         );
15048         this.setYConstraint(topSpace - (pad.top||0), //top
15049                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15050         );
15051     },
15052
15053     /**
15054      * Returns a reference to the linked element
15055      * @method getEl
15056      * @return {HTMLElement} the html element
15057      */
15058     getEl: function() {
15059         if (!this._domRef) {
15060             this._domRef = Roo.getDom(this.id);
15061         }
15062
15063         return this._domRef;
15064     },
15065
15066     /**
15067      * Returns a reference to the actual element to drag.  By default this is
15068      * the same as the html element, but it can be assigned to another
15069      * element. An example of this can be found in Roo.dd.DDProxy
15070      * @method getDragEl
15071      * @return {HTMLElement} the html element
15072      */
15073     getDragEl: function() {
15074         return Roo.getDom(this.dragElId);
15075     },
15076
15077     /**
15078      * Sets up the DragDrop object.  Must be called in the constructor of any
15079      * Roo.dd.DragDrop subclass
15080      * @method init
15081      * @param id the id of the linked element
15082      * @param {String} sGroup the group of related items
15083      * @param {object} config configuration attributes
15084      */
15085     init: function(id, sGroup, config) {
15086         this.initTarget(id, sGroup, config);
15087         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15088         // Event.on(this.id, "selectstart", Event.preventDefault);
15089     },
15090
15091     /**
15092      * Initializes Targeting functionality only... the object does not
15093      * get a mousedown handler.
15094      * @method initTarget
15095      * @param id the id of the linked element
15096      * @param {String} sGroup the group of related items
15097      * @param {object} config configuration attributes
15098      */
15099     initTarget: function(id, sGroup, config) {
15100
15101         // configuration attributes
15102         this.config = config || {};
15103
15104         // create a local reference to the drag and drop manager
15105         this.DDM = Roo.dd.DDM;
15106         // initialize the groups array
15107         this.groups = {};
15108
15109         // assume that we have an element reference instead of an id if the
15110         // parameter is not a string
15111         if (typeof id !== "string") {
15112             id = Roo.id(id);
15113         }
15114
15115         // set the id
15116         this.id = id;
15117
15118         // add to an interaction group
15119         this.addToGroup((sGroup) ? sGroup : "default");
15120
15121         // We don't want to register this as the handle with the manager
15122         // so we just set the id rather than calling the setter.
15123         this.handleElId = id;
15124
15125         // the linked element is the element that gets dragged by default
15126         this.setDragElId(id);
15127
15128         // by default, clicked anchors will not start drag operations.
15129         this.invalidHandleTypes = { A: "A" };
15130         this.invalidHandleIds = {};
15131         this.invalidHandleClasses = [];
15132
15133         this.applyConfig();
15134
15135         this.handleOnAvailable();
15136     },
15137
15138     /**
15139      * Applies the configuration parameters that were passed into the constructor.
15140      * This is supposed to happen at each level through the inheritance chain.  So
15141      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15142      * DragDrop in order to get all of the parameters that are available in
15143      * each object.
15144      * @method applyConfig
15145      */
15146     applyConfig: function() {
15147
15148         // configurable properties:
15149         //    padding, isTarget, maintainOffset, primaryButtonOnly
15150         this.padding           = this.config.padding || [0, 0, 0, 0];
15151         this.isTarget          = (this.config.isTarget !== false);
15152         this.maintainOffset    = (this.config.maintainOffset);
15153         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15154
15155     },
15156
15157     /**
15158      * Executed when the linked element is available
15159      * @method handleOnAvailable
15160      * @private
15161      */
15162     handleOnAvailable: function() {
15163         this.available = true;
15164         this.resetConstraints();
15165         this.onAvailable();
15166     },
15167
15168      /**
15169      * Configures the padding for the target zone in px.  Effectively expands
15170      * (or reduces) the virtual object size for targeting calculations.
15171      * Supports css-style shorthand; if only one parameter is passed, all sides
15172      * will have that padding, and if only two are passed, the top and bottom
15173      * will have the first param, the left and right the second.
15174      * @method setPadding
15175      * @param {int} iTop    Top pad
15176      * @param {int} iRight  Right pad
15177      * @param {int} iBot    Bot pad
15178      * @param {int} iLeft   Left pad
15179      */
15180     setPadding: function(iTop, iRight, iBot, iLeft) {
15181         // this.padding = [iLeft, iRight, iTop, iBot];
15182         if (!iRight && 0 !== iRight) {
15183             this.padding = [iTop, iTop, iTop, iTop];
15184         } else if (!iBot && 0 !== iBot) {
15185             this.padding = [iTop, iRight, iTop, iRight];
15186         } else {
15187             this.padding = [iTop, iRight, iBot, iLeft];
15188         }
15189     },
15190
15191     /**
15192      * Stores the initial placement of the linked element.
15193      * @method setInitialPosition
15194      * @param {int} diffX   the X offset, default 0
15195      * @param {int} diffY   the Y offset, default 0
15196      */
15197     setInitPosition: function(diffX, diffY) {
15198         var el = this.getEl();
15199
15200         if (!this.DDM.verifyEl(el)) {
15201             return;
15202         }
15203
15204         var dx = diffX || 0;
15205         var dy = diffY || 0;
15206
15207         var p = Dom.getXY( el );
15208
15209         this.initPageX = p[0] - dx;
15210         this.initPageY = p[1] - dy;
15211
15212         this.lastPageX = p[0];
15213         this.lastPageY = p[1];
15214
15215
15216         this.setStartPosition(p);
15217     },
15218
15219     /**
15220      * Sets the start position of the element.  This is set when the obj
15221      * is initialized, the reset when a drag is started.
15222      * @method setStartPosition
15223      * @param pos current position (from previous lookup)
15224      * @private
15225      */
15226     setStartPosition: function(pos) {
15227         var p = pos || Dom.getXY( this.getEl() );
15228         this.deltaSetXY = null;
15229
15230         this.startPageX = p[0];
15231         this.startPageY = p[1];
15232     },
15233
15234     /**
15235      * Add this instance to a group of related drag/drop objects.  All
15236      * instances belong to at least one group, and can belong to as many
15237      * groups as needed.
15238      * @method addToGroup
15239      * @param sGroup {string} the name of the group
15240      */
15241     addToGroup: function(sGroup) {
15242         this.groups[sGroup] = true;
15243         this.DDM.regDragDrop(this, sGroup);
15244     },
15245
15246     /**
15247      * Remove's this instance from the supplied interaction group
15248      * @method removeFromGroup
15249      * @param {string}  sGroup  The group to drop
15250      */
15251     removeFromGroup: function(sGroup) {
15252         if (this.groups[sGroup]) {
15253             delete this.groups[sGroup];
15254         }
15255
15256         this.DDM.removeDDFromGroup(this, sGroup);
15257     },
15258
15259     /**
15260      * Allows you to specify that an element other than the linked element
15261      * will be moved with the cursor during a drag
15262      * @method setDragElId
15263      * @param id {string} the id of the element that will be used to initiate the drag
15264      */
15265     setDragElId: function(id) {
15266         this.dragElId = id;
15267     },
15268
15269     /**
15270      * Allows you to specify a child of the linked element that should be
15271      * used to initiate the drag operation.  An example of this would be if
15272      * you have a content div with text and links.  Clicking anywhere in the
15273      * content area would normally start the drag operation.  Use this method
15274      * to specify that an element inside of the content div is the element
15275      * that starts the drag operation.
15276      * @method setHandleElId
15277      * @param id {string} the id of the element that will be used to
15278      * initiate the drag.
15279      */
15280     setHandleElId: function(id) {
15281         if (typeof id !== "string") {
15282             id = Roo.id(id);
15283         }
15284         this.handleElId = id;
15285         this.DDM.regHandle(this.id, id);
15286     },
15287
15288     /**
15289      * Allows you to set an element outside of the linked element as a drag
15290      * handle
15291      * @method setOuterHandleElId
15292      * @param id the id of the element that will be used to initiate the drag
15293      */
15294     setOuterHandleElId: function(id) {
15295         if (typeof id !== "string") {
15296             id = Roo.id(id);
15297         }
15298         Event.on(id, "mousedown",
15299                 this.handleMouseDown, this);
15300         this.setHandleElId(id);
15301
15302         this.hasOuterHandles = true;
15303     },
15304
15305     /**
15306      * Remove all drag and drop hooks for this element
15307      * @method unreg
15308      */
15309     unreg: function() {
15310         Event.un(this.id, "mousedown",
15311                 this.handleMouseDown);
15312         this._domRef = null;
15313         this.DDM._remove(this);
15314     },
15315
15316     destroy : function(){
15317         this.unreg();
15318     },
15319
15320     /**
15321      * Returns true if this instance is locked, or the drag drop mgr is locked
15322      * (meaning that all drag/drop is disabled on the page.)
15323      * @method isLocked
15324      * @return {boolean} true if this obj or all drag/drop is locked, else
15325      * false
15326      */
15327     isLocked: function() {
15328         return (this.DDM.isLocked() || this.locked);
15329     },
15330
15331     /**
15332      * Fired when this object is clicked
15333      * @method handleMouseDown
15334      * @param {Event} e
15335      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15336      * @private
15337      */
15338     handleMouseDown: function(e, oDD){
15339         if (this.primaryButtonOnly && e.button != 0) {
15340             return;
15341         }
15342
15343         if (this.isLocked()) {
15344             return;
15345         }
15346
15347         this.DDM.refreshCache(this.groups);
15348
15349         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15350         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15351         } else {
15352             if (this.clickValidator(e)) {
15353
15354                 // set the initial element position
15355                 this.setStartPosition();
15356
15357
15358                 this.b4MouseDown(e);
15359                 this.onMouseDown(e);
15360
15361                 this.DDM.handleMouseDown(e, this);
15362
15363                 this.DDM.stopEvent(e);
15364             } else {
15365
15366
15367             }
15368         }
15369     },
15370
15371     clickValidator: function(e) {
15372         var target = e.getTarget();
15373         return ( this.isValidHandleChild(target) &&
15374                     (this.id == this.handleElId ||
15375                         this.DDM.handleWasClicked(target, this.id)) );
15376     },
15377
15378     /**
15379      * Allows you to specify a tag name that should not start a drag operation
15380      * when clicked.  This is designed to facilitate embedding links within a
15381      * drag handle that do something other than start the drag.
15382      * @method addInvalidHandleType
15383      * @param {string} tagName the type of element to exclude
15384      */
15385     addInvalidHandleType: function(tagName) {
15386         var type = tagName.toUpperCase();
15387         this.invalidHandleTypes[type] = type;
15388     },
15389
15390     /**
15391      * Lets you to specify an element id for a child of a drag handle
15392      * that should not initiate a drag
15393      * @method addInvalidHandleId
15394      * @param {string} id the element id of the element you wish to ignore
15395      */
15396     addInvalidHandleId: function(id) {
15397         if (typeof id !== "string") {
15398             id = Roo.id(id);
15399         }
15400         this.invalidHandleIds[id] = id;
15401     },
15402
15403     /**
15404      * Lets you specify a css class of elements that will not initiate a drag
15405      * @method addInvalidHandleClass
15406      * @param {string} cssClass the class of the elements you wish to ignore
15407      */
15408     addInvalidHandleClass: function(cssClass) {
15409         this.invalidHandleClasses.push(cssClass);
15410     },
15411
15412     /**
15413      * Unsets an excluded tag name set by addInvalidHandleType
15414      * @method removeInvalidHandleType
15415      * @param {string} tagName the type of element to unexclude
15416      */
15417     removeInvalidHandleType: function(tagName) {
15418         var type = tagName.toUpperCase();
15419         // this.invalidHandleTypes[type] = null;
15420         delete this.invalidHandleTypes[type];
15421     },
15422
15423     /**
15424      * Unsets an invalid handle id
15425      * @method removeInvalidHandleId
15426      * @param {string} id the id of the element to re-enable
15427      */
15428     removeInvalidHandleId: function(id) {
15429         if (typeof id !== "string") {
15430             id = Roo.id(id);
15431         }
15432         delete this.invalidHandleIds[id];
15433     },
15434
15435     /**
15436      * Unsets an invalid css class
15437      * @method removeInvalidHandleClass
15438      * @param {string} cssClass the class of the element(s) you wish to
15439      * re-enable
15440      */
15441     removeInvalidHandleClass: function(cssClass) {
15442         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15443             if (this.invalidHandleClasses[i] == cssClass) {
15444                 delete this.invalidHandleClasses[i];
15445             }
15446         }
15447     },
15448
15449     /**
15450      * Checks the tag exclusion list to see if this click should be ignored
15451      * @method isValidHandleChild
15452      * @param {HTMLElement} node the HTMLElement to evaluate
15453      * @return {boolean} true if this is a valid tag type, false if not
15454      */
15455     isValidHandleChild: function(node) {
15456
15457         var valid = true;
15458         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15459         var nodeName;
15460         try {
15461             nodeName = node.nodeName.toUpperCase();
15462         } catch(e) {
15463             nodeName = node.nodeName;
15464         }
15465         valid = valid && !this.invalidHandleTypes[nodeName];
15466         valid = valid && !this.invalidHandleIds[node.id];
15467
15468         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15469             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15470         }
15471
15472
15473         return valid;
15474
15475     },
15476
15477     /**
15478      * Create the array of horizontal tick marks if an interval was specified
15479      * in setXConstraint().
15480      * @method setXTicks
15481      * @private
15482      */
15483     setXTicks: function(iStartX, iTickSize) {
15484         this.xTicks = [];
15485         this.xTickSize = iTickSize;
15486
15487         var tickMap = {};
15488
15489         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15490             if (!tickMap[i]) {
15491                 this.xTicks[this.xTicks.length] = i;
15492                 tickMap[i] = true;
15493             }
15494         }
15495
15496         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15497             if (!tickMap[i]) {
15498                 this.xTicks[this.xTicks.length] = i;
15499                 tickMap[i] = true;
15500             }
15501         }
15502
15503         this.xTicks.sort(this.DDM.numericSort) ;
15504     },
15505
15506     /**
15507      * Create the array of vertical tick marks if an interval was specified in
15508      * setYConstraint().
15509      * @method setYTicks
15510      * @private
15511      */
15512     setYTicks: function(iStartY, iTickSize) {
15513         this.yTicks = [];
15514         this.yTickSize = iTickSize;
15515
15516         var tickMap = {};
15517
15518         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15519             if (!tickMap[i]) {
15520                 this.yTicks[this.yTicks.length] = i;
15521                 tickMap[i] = true;
15522             }
15523         }
15524
15525         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15526             if (!tickMap[i]) {
15527                 this.yTicks[this.yTicks.length] = i;
15528                 tickMap[i] = true;
15529             }
15530         }
15531
15532         this.yTicks.sort(this.DDM.numericSort) ;
15533     },
15534
15535     /**
15536      * By default, the element can be dragged any place on the screen.  Use
15537      * this method to limit the horizontal travel of the element.  Pass in
15538      * 0,0 for the parameters if you want to lock the drag to the y axis.
15539      * @method setXConstraint
15540      * @param {int} iLeft the number of pixels the element can move to the left
15541      * @param {int} iRight the number of pixels the element can move to the
15542      * right
15543      * @param {int} iTickSize optional parameter for specifying that the
15544      * element
15545      * should move iTickSize pixels at a time.
15546      */
15547     setXConstraint: function(iLeft, iRight, iTickSize) {
15548         this.leftConstraint = iLeft;
15549         this.rightConstraint = iRight;
15550
15551         this.minX = this.initPageX - iLeft;
15552         this.maxX = this.initPageX + iRight;
15553         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15554
15555         this.constrainX = true;
15556     },
15557
15558     /**
15559      * Clears any constraints applied to this instance.  Also clears ticks
15560      * since they can't exist independent of a constraint at this time.
15561      * @method clearConstraints
15562      */
15563     clearConstraints: function() {
15564         this.constrainX = false;
15565         this.constrainY = false;
15566         this.clearTicks();
15567     },
15568
15569     /**
15570      * Clears any tick interval defined for this instance
15571      * @method clearTicks
15572      */
15573     clearTicks: function() {
15574         this.xTicks = null;
15575         this.yTicks = null;
15576         this.xTickSize = 0;
15577         this.yTickSize = 0;
15578     },
15579
15580     /**
15581      * By default, the element can be dragged any place on the screen.  Set
15582      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15583      * parameters if you want to lock the drag to the x axis.
15584      * @method setYConstraint
15585      * @param {int} iUp the number of pixels the element can move up
15586      * @param {int} iDown the number of pixels the element can move down
15587      * @param {int} iTickSize optional parameter for specifying that the
15588      * element should move iTickSize pixels at a time.
15589      */
15590     setYConstraint: function(iUp, iDown, iTickSize) {
15591         this.topConstraint = iUp;
15592         this.bottomConstraint = iDown;
15593
15594         this.minY = this.initPageY - iUp;
15595         this.maxY = this.initPageY + iDown;
15596         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15597
15598         this.constrainY = true;
15599
15600     },
15601
15602     /**
15603      * resetConstraints must be called if you manually reposition a dd element.
15604      * @method resetConstraints
15605      * @param {boolean} maintainOffset
15606      */
15607     resetConstraints: function() {
15608
15609
15610         // Maintain offsets if necessary
15611         if (this.initPageX || this.initPageX === 0) {
15612             // figure out how much this thing has moved
15613             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15614             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15615
15616             this.setInitPosition(dx, dy);
15617
15618         // This is the first time we have detected the element's position
15619         } else {
15620             this.setInitPosition();
15621         }
15622
15623         if (this.constrainX) {
15624             this.setXConstraint( this.leftConstraint,
15625                                  this.rightConstraint,
15626                                  this.xTickSize        );
15627         }
15628
15629         if (this.constrainY) {
15630             this.setYConstraint( this.topConstraint,
15631                                  this.bottomConstraint,
15632                                  this.yTickSize         );
15633         }
15634     },
15635
15636     /**
15637      * Normally the drag element is moved pixel by pixel, but we can specify
15638      * that it move a number of pixels at a time.  This method resolves the
15639      * location when we have it set up like this.
15640      * @method getTick
15641      * @param {int} val where we want to place the object
15642      * @param {int[]} tickArray sorted array of valid points
15643      * @return {int} the closest tick
15644      * @private
15645      */
15646     getTick: function(val, tickArray) {
15647
15648         if (!tickArray) {
15649             // If tick interval is not defined, it is effectively 1 pixel,
15650             // so we return the value passed to us.
15651             return val;
15652         } else if (tickArray[0] >= val) {
15653             // The value is lower than the first tick, so we return the first
15654             // tick.
15655             return tickArray[0];
15656         } else {
15657             for (var i=0, len=tickArray.length; i<len; ++i) {
15658                 var next = i + 1;
15659                 if (tickArray[next] && tickArray[next] >= val) {
15660                     var diff1 = val - tickArray[i];
15661                     var diff2 = tickArray[next] - val;
15662                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15663                 }
15664             }
15665
15666             // The value is larger than the last tick, so we return the last
15667             // tick.
15668             return tickArray[tickArray.length - 1];
15669         }
15670     },
15671
15672     /**
15673      * toString method
15674      * @method toString
15675      * @return {string} string representation of the dd obj
15676      */
15677     toString: function() {
15678         return ("DragDrop " + this.id);
15679     }
15680
15681 });
15682
15683 })();
15684 /*
15685  * Based on:
15686  * Ext JS Library 1.1.1
15687  * Copyright(c) 2006-2007, Ext JS, LLC.
15688  *
15689  * Originally Released Under LGPL - original licence link has changed is not relivant.
15690  *
15691  * Fork - LGPL
15692  * <script type="text/javascript">
15693  */
15694
15695
15696 /**
15697  * The drag and drop utility provides a framework for building drag and drop
15698  * applications.  In addition to enabling drag and drop for specific elements,
15699  * the drag and drop elements are tracked by the manager class, and the
15700  * interactions between the various elements are tracked during the drag and
15701  * the implementing code is notified about these important moments.
15702  */
15703
15704 // Only load the library once.  Rewriting the manager class would orphan
15705 // existing drag and drop instances.
15706 if (!Roo.dd.DragDropMgr) {
15707
15708 /**
15709  * @class Roo.dd.DragDropMgr
15710  * DragDropMgr is a singleton that tracks the element interaction for
15711  * all DragDrop items in the window.  Generally, you will not call
15712  * this class directly, but it does have helper methods that could
15713  * be useful in your DragDrop implementations.
15714  * @singleton
15715  */
15716 Roo.dd.DragDropMgr = function() {
15717
15718     var Event = Roo.EventManager;
15719
15720     return {
15721
15722         /**
15723          * Two dimensional Array of registered DragDrop objects.  The first
15724          * dimension is the DragDrop item group, the second the DragDrop
15725          * object.
15726          * @property ids
15727          * @type {string: string}
15728          * @private
15729          * @static
15730          */
15731         ids: {},
15732
15733         /**
15734          * Array of element ids defined as drag handles.  Used to determine
15735          * if the element that generated the mousedown event is actually the
15736          * handle and not the html element itself.
15737          * @property handleIds
15738          * @type {string: string}
15739          * @private
15740          * @static
15741          */
15742         handleIds: {},
15743
15744         /**
15745          * the DragDrop object that is currently being dragged
15746          * @property dragCurrent
15747          * @type DragDrop
15748          * @private
15749          * @static
15750          **/
15751         dragCurrent: null,
15752
15753         /**
15754          * the DragDrop object(s) that are being hovered over
15755          * @property dragOvers
15756          * @type Array
15757          * @private
15758          * @static
15759          */
15760         dragOvers: {},
15761
15762         /**
15763          * the X distance between the cursor and the object being dragged
15764          * @property deltaX
15765          * @type int
15766          * @private
15767          * @static
15768          */
15769         deltaX: 0,
15770
15771         /**
15772          * the Y distance between the cursor and the object being dragged
15773          * @property deltaY
15774          * @type int
15775          * @private
15776          * @static
15777          */
15778         deltaY: 0,
15779
15780         /**
15781          * Flag to determine if we should prevent the default behavior of the
15782          * events we define. By default this is true, but this can be set to
15783          * false if you need the default behavior (not recommended)
15784          * @property preventDefault
15785          * @type boolean
15786          * @static
15787          */
15788         preventDefault: true,
15789
15790         /**
15791          * Flag to determine if we should stop the propagation of the events
15792          * we generate. This is true by default but you may want to set it to
15793          * false if the html element contains other features that require the
15794          * mouse click.
15795          * @property stopPropagation
15796          * @type boolean
15797          * @static
15798          */
15799         stopPropagation: true,
15800
15801         /**
15802          * Internal flag that is set to true when drag and drop has been
15803          * intialized
15804          * @property initialized
15805          * @private
15806          * @static
15807          */
15808         initalized: false,
15809
15810         /**
15811          * All drag and drop can be disabled.
15812          * @property locked
15813          * @private
15814          * @static
15815          */
15816         locked: false,
15817
15818         /**
15819          * Called the first time an element is registered.
15820          * @method init
15821          * @private
15822          * @static
15823          */
15824         init: function() {
15825             this.initialized = true;
15826         },
15827
15828         /**
15829          * In point mode, drag and drop interaction is defined by the
15830          * location of the cursor during the drag/drop
15831          * @property POINT
15832          * @type int
15833          * @static
15834          */
15835         POINT: 0,
15836
15837         /**
15838          * In intersect mode, drag and drop interactio nis defined by the
15839          * overlap of two or more drag and drop objects.
15840          * @property INTERSECT
15841          * @type int
15842          * @static
15843          */
15844         INTERSECT: 1,
15845
15846         /**
15847          * The current drag and drop mode.  Default: POINT
15848          * @property mode
15849          * @type int
15850          * @static
15851          */
15852         mode: 0,
15853
15854         /**
15855          * Runs method on all drag and drop objects
15856          * @method _execOnAll
15857          * @private
15858          * @static
15859          */
15860         _execOnAll: function(sMethod, args) {
15861             for (var i in this.ids) {
15862                 for (var j in this.ids[i]) {
15863                     var oDD = this.ids[i][j];
15864                     if (! this.isTypeOfDD(oDD)) {
15865                         continue;
15866                     }
15867                     oDD[sMethod].apply(oDD, args);
15868                 }
15869             }
15870         },
15871
15872         /**
15873          * Drag and drop initialization.  Sets up the global event handlers
15874          * @method _onLoad
15875          * @private
15876          * @static
15877          */
15878         _onLoad: function() {
15879
15880             this.init();
15881
15882
15883             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15884             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15885             Event.on(window,   "unload",    this._onUnload, this, true);
15886             Event.on(window,   "resize",    this._onResize, this, true);
15887             // Event.on(window,   "mouseout",    this._test);
15888
15889         },
15890
15891         /**
15892          * Reset constraints on all drag and drop objs
15893          * @method _onResize
15894          * @private
15895          * @static
15896          */
15897         _onResize: function(e) {
15898             this._execOnAll("resetConstraints", []);
15899         },
15900
15901         /**
15902          * Lock all drag and drop functionality
15903          * @method lock
15904          * @static
15905          */
15906         lock: function() { this.locked = true; },
15907
15908         /**
15909          * Unlock all drag and drop functionality
15910          * @method unlock
15911          * @static
15912          */
15913         unlock: function() { this.locked = false; },
15914
15915         /**
15916          * Is drag and drop locked?
15917          * @method isLocked
15918          * @return {boolean} True if drag and drop is locked, false otherwise.
15919          * @static
15920          */
15921         isLocked: function() { return this.locked; },
15922
15923         /**
15924          * Location cache that is set for all drag drop objects when a drag is
15925          * initiated, cleared when the drag is finished.
15926          * @property locationCache
15927          * @private
15928          * @static
15929          */
15930         locationCache: {},
15931
15932         /**
15933          * Set useCache to false if you want to force object the lookup of each
15934          * drag and drop linked element constantly during a drag.
15935          * @property useCache
15936          * @type boolean
15937          * @static
15938          */
15939         useCache: true,
15940
15941         /**
15942          * The number of pixels that the mouse needs to move after the
15943          * mousedown before the drag is initiated.  Default=3;
15944          * @property clickPixelThresh
15945          * @type int
15946          * @static
15947          */
15948         clickPixelThresh: 3,
15949
15950         /**
15951          * The number of milliseconds after the mousedown event to initiate the
15952          * drag if we don't get a mouseup event. Default=1000
15953          * @property clickTimeThresh
15954          * @type int
15955          * @static
15956          */
15957         clickTimeThresh: 350,
15958
15959         /**
15960          * Flag that indicates that either the drag pixel threshold or the
15961          * mousdown time threshold has been met
15962          * @property dragThreshMet
15963          * @type boolean
15964          * @private
15965          * @static
15966          */
15967         dragThreshMet: false,
15968
15969         /**
15970          * Timeout used for the click time threshold
15971          * @property clickTimeout
15972          * @type Object
15973          * @private
15974          * @static
15975          */
15976         clickTimeout: null,
15977
15978         /**
15979          * The X position of the mousedown event stored for later use when a
15980          * drag threshold is met.
15981          * @property startX
15982          * @type int
15983          * @private
15984          * @static
15985          */
15986         startX: 0,
15987
15988         /**
15989          * The Y position of the mousedown event stored for later use when a
15990          * drag threshold is met.
15991          * @property startY
15992          * @type int
15993          * @private
15994          * @static
15995          */
15996         startY: 0,
15997
15998         /**
15999          * Each DragDrop instance must be registered with the DragDropMgr.
16000          * This is executed in DragDrop.init()
16001          * @method regDragDrop
16002          * @param {DragDrop} oDD the DragDrop object to register
16003          * @param {String} sGroup the name of the group this element belongs to
16004          * @static
16005          */
16006         regDragDrop: function(oDD, sGroup) {
16007             if (!this.initialized) { this.init(); }
16008
16009             if (!this.ids[sGroup]) {
16010                 this.ids[sGroup] = {};
16011             }
16012             this.ids[sGroup][oDD.id] = oDD;
16013         },
16014
16015         /**
16016          * Removes the supplied dd instance from the supplied group. Executed
16017          * by DragDrop.removeFromGroup, so don't call this function directly.
16018          * @method removeDDFromGroup
16019          * @private
16020          * @static
16021          */
16022         removeDDFromGroup: function(oDD, sGroup) {
16023             if (!this.ids[sGroup]) {
16024                 this.ids[sGroup] = {};
16025             }
16026
16027             var obj = this.ids[sGroup];
16028             if (obj && obj[oDD.id]) {
16029                 delete obj[oDD.id];
16030             }
16031         },
16032
16033         /**
16034          * Unregisters a drag and drop item.  This is executed in
16035          * DragDrop.unreg, use that method instead of calling this directly.
16036          * @method _remove
16037          * @private
16038          * @static
16039          */
16040         _remove: function(oDD) {
16041             for (var g in oDD.groups) {
16042                 if (g && this.ids[g][oDD.id]) {
16043                     delete this.ids[g][oDD.id];
16044                 }
16045             }
16046             delete this.handleIds[oDD.id];
16047         },
16048
16049         /**
16050          * Each DragDrop handle element must be registered.  This is done
16051          * automatically when executing DragDrop.setHandleElId()
16052          * @method regHandle
16053          * @param {String} sDDId the DragDrop id this element is a handle for
16054          * @param {String} sHandleId the id of the element that is the drag
16055          * handle
16056          * @static
16057          */
16058         regHandle: function(sDDId, sHandleId) {
16059             if (!this.handleIds[sDDId]) {
16060                 this.handleIds[sDDId] = {};
16061             }
16062             this.handleIds[sDDId][sHandleId] = sHandleId;
16063         },
16064
16065         /**
16066          * Utility function to determine if a given element has been
16067          * registered as a drag drop item.
16068          * @method isDragDrop
16069          * @param {String} id the element id to check
16070          * @return {boolean} true if this element is a DragDrop item,
16071          * false otherwise
16072          * @static
16073          */
16074         isDragDrop: function(id) {
16075             return ( this.getDDById(id) ) ? true : false;
16076         },
16077
16078         /**
16079          * Returns the drag and drop instances that are in all groups the
16080          * passed in instance belongs to.
16081          * @method getRelated
16082          * @param {DragDrop} p_oDD the obj to get related data for
16083          * @param {boolean} bTargetsOnly if true, only return targetable objs
16084          * @return {DragDrop[]} the related instances
16085          * @static
16086          */
16087         getRelated: function(p_oDD, bTargetsOnly) {
16088             var oDDs = [];
16089             for (var i in p_oDD.groups) {
16090                 for (j in this.ids[i]) {
16091                     var dd = this.ids[i][j];
16092                     if (! this.isTypeOfDD(dd)) {
16093                         continue;
16094                     }
16095                     if (!bTargetsOnly || dd.isTarget) {
16096                         oDDs[oDDs.length] = dd;
16097                     }
16098                 }
16099             }
16100
16101             return oDDs;
16102         },
16103
16104         /**
16105          * Returns true if the specified dd target is a legal target for
16106          * the specifice drag obj
16107          * @method isLegalTarget
16108          * @param {DragDrop} the drag obj
16109          * @param {DragDrop} the target
16110          * @return {boolean} true if the target is a legal target for the
16111          * dd obj
16112          * @static
16113          */
16114         isLegalTarget: function (oDD, oTargetDD) {
16115             var targets = this.getRelated(oDD, true);
16116             for (var i=0, len=targets.length;i<len;++i) {
16117                 if (targets[i].id == oTargetDD.id) {
16118                     return true;
16119                 }
16120             }
16121
16122             return false;
16123         },
16124
16125         /**
16126          * My goal is to be able to transparently determine if an object is
16127          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16128          * returns "object", oDD.constructor.toString() always returns
16129          * "DragDrop" and not the name of the subclass.  So for now it just
16130          * evaluates a well-known variable in DragDrop.
16131          * @method isTypeOfDD
16132          * @param {Object} the object to evaluate
16133          * @return {boolean} true if typeof oDD = DragDrop
16134          * @static
16135          */
16136         isTypeOfDD: function (oDD) {
16137             return (oDD && oDD.__ygDragDrop);
16138         },
16139
16140         /**
16141          * Utility function to determine if a given element has been
16142          * registered as a drag drop handle for the given Drag Drop object.
16143          * @method isHandle
16144          * @param {String} id the element id to check
16145          * @return {boolean} true if this element is a DragDrop handle, false
16146          * otherwise
16147          * @static
16148          */
16149         isHandle: function(sDDId, sHandleId) {
16150             return ( this.handleIds[sDDId] &&
16151                             this.handleIds[sDDId][sHandleId] );
16152         },
16153
16154         /**
16155          * Returns the DragDrop instance for a given id
16156          * @method getDDById
16157          * @param {String} id the id of the DragDrop object
16158          * @return {DragDrop} the drag drop object, null if it is not found
16159          * @static
16160          */
16161         getDDById: function(id) {
16162             for (var i in this.ids) {
16163                 if (this.ids[i][id]) {
16164                     return this.ids[i][id];
16165                 }
16166             }
16167             return null;
16168         },
16169
16170         /**
16171          * Fired after a registered DragDrop object gets the mousedown event.
16172          * Sets up the events required to track the object being dragged
16173          * @method handleMouseDown
16174          * @param {Event} e the event
16175          * @param oDD the DragDrop object being dragged
16176          * @private
16177          * @static
16178          */
16179         handleMouseDown: function(e, oDD) {
16180             if(Roo.QuickTips){
16181                 Roo.QuickTips.disable();
16182             }
16183             this.currentTarget = e.getTarget();
16184
16185             this.dragCurrent = oDD;
16186
16187             var el = oDD.getEl();
16188
16189             // track start position
16190             this.startX = e.getPageX();
16191             this.startY = e.getPageY();
16192
16193             this.deltaX = this.startX - el.offsetLeft;
16194             this.deltaY = this.startY - el.offsetTop;
16195
16196             this.dragThreshMet = false;
16197
16198             this.clickTimeout = setTimeout(
16199                     function() {
16200                         var DDM = Roo.dd.DDM;
16201                         DDM.startDrag(DDM.startX, DDM.startY);
16202                     },
16203                     this.clickTimeThresh );
16204         },
16205
16206         /**
16207          * Fired when either the drag pixel threshol or the mousedown hold
16208          * time threshold has been met.
16209          * @method startDrag
16210          * @param x {int} the X position of the original mousedown
16211          * @param y {int} the Y position of the original mousedown
16212          * @static
16213          */
16214         startDrag: function(x, y) {
16215             clearTimeout(this.clickTimeout);
16216             if (this.dragCurrent) {
16217                 this.dragCurrent.b4StartDrag(x, y);
16218                 this.dragCurrent.startDrag(x, y);
16219             }
16220             this.dragThreshMet = true;
16221         },
16222
16223         /**
16224          * Internal function to handle the mouseup event.  Will be invoked
16225          * from the context of the document.
16226          * @method handleMouseUp
16227          * @param {Event} e the event
16228          * @private
16229          * @static
16230          */
16231         handleMouseUp: function(e) {
16232
16233             if(Roo.QuickTips){
16234                 Roo.QuickTips.enable();
16235             }
16236             if (! this.dragCurrent) {
16237                 return;
16238             }
16239
16240             clearTimeout(this.clickTimeout);
16241
16242             if (this.dragThreshMet) {
16243                 this.fireEvents(e, true);
16244             } else {
16245             }
16246
16247             this.stopDrag(e);
16248
16249             this.stopEvent(e);
16250         },
16251
16252         /**
16253          * Utility to stop event propagation and event default, if these
16254          * features are turned on.
16255          * @method stopEvent
16256          * @param {Event} e the event as returned by this.getEvent()
16257          * @static
16258          */
16259         stopEvent: function(e){
16260             if(this.stopPropagation) {
16261                 e.stopPropagation();
16262             }
16263
16264             if (this.preventDefault) {
16265                 e.preventDefault();
16266             }
16267         },
16268
16269         /**
16270          * Internal function to clean up event handlers after the drag
16271          * operation is complete
16272          * @method stopDrag
16273          * @param {Event} e the event
16274          * @private
16275          * @static
16276          */
16277         stopDrag: function(e) {
16278             // Fire the drag end event for the item that was dragged
16279             if (this.dragCurrent) {
16280                 if (this.dragThreshMet) {
16281                     this.dragCurrent.b4EndDrag(e);
16282                     this.dragCurrent.endDrag(e);
16283                 }
16284
16285                 this.dragCurrent.onMouseUp(e);
16286             }
16287
16288             this.dragCurrent = null;
16289             this.dragOvers = {};
16290         },
16291
16292         /**
16293          * Internal function to handle the mousemove event.  Will be invoked
16294          * from the context of the html element.
16295          *
16296          * @TODO figure out what we can do about mouse events lost when the
16297          * user drags objects beyond the window boundary.  Currently we can
16298          * detect this in internet explorer by verifying that the mouse is
16299          * down during the mousemove event.  Firefox doesn't give us the
16300          * button state on the mousemove event.
16301          * @method handleMouseMove
16302          * @param {Event} e the event
16303          * @private
16304          * @static
16305          */
16306         handleMouseMove: function(e) {
16307             if (! this.dragCurrent) {
16308                 return true;
16309             }
16310
16311             // var button = e.which || e.button;
16312
16313             // check for IE mouseup outside of page boundary
16314             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16315                 this.stopEvent(e);
16316                 return this.handleMouseUp(e);
16317             }
16318
16319             if (!this.dragThreshMet) {
16320                 var diffX = Math.abs(this.startX - e.getPageX());
16321                 var diffY = Math.abs(this.startY - e.getPageY());
16322                 if (diffX > this.clickPixelThresh ||
16323                             diffY > this.clickPixelThresh) {
16324                     this.startDrag(this.startX, this.startY);
16325                 }
16326             }
16327
16328             if (this.dragThreshMet) {
16329                 this.dragCurrent.b4Drag(e);
16330                 this.dragCurrent.onDrag(e);
16331                 if(!this.dragCurrent.moveOnly){
16332                     this.fireEvents(e, false);
16333                 }
16334             }
16335
16336             this.stopEvent(e);
16337
16338             return true;
16339         },
16340
16341         /**
16342          * Iterates over all of the DragDrop elements to find ones we are
16343          * hovering over or dropping on
16344          * @method fireEvents
16345          * @param {Event} e the event
16346          * @param {boolean} isDrop is this a drop op or a mouseover op?
16347          * @private
16348          * @static
16349          */
16350         fireEvents: function(e, isDrop) {
16351             var dc = this.dragCurrent;
16352
16353             // If the user did the mouse up outside of the window, we could
16354             // get here even though we have ended the drag.
16355             if (!dc || dc.isLocked()) {
16356                 return;
16357             }
16358
16359             var pt = e.getPoint();
16360
16361             // cache the previous dragOver array
16362             var oldOvers = [];
16363
16364             var outEvts   = [];
16365             var overEvts  = [];
16366             var dropEvts  = [];
16367             var enterEvts = [];
16368
16369             // Check to see if the object(s) we were hovering over is no longer
16370             // being hovered over so we can fire the onDragOut event
16371             for (var i in this.dragOvers) {
16372
16373                 var ddo = this.dragOvers[i];
16374
16375                 if (! this.isTypeOfDD(ddo)) {
16376                     continue;
16377                 }
16378
16379                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16380                     outEvts.push( ddo );
16381                 }
16382
16383                 oldOvers[i] = true;
16384                 delete this.dragOvers[i];
16385             }
16386
16387             for (var sGroup in dc.groups) {
16388
16389                 if ("string" != typeof sGroup) {
16390                     continue;
16391                 }
16392
16393                 for (i in this.ids[sGroup]) {
16394                     var oDD = this.ids[sGroup][i];
16395                     if (! this.isTypeOfDD(oDD)) {
16396                         continue;
16397                     }
16398
16399                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16400                         if (this.isOverTarget(pt, oDD, this.mode)) {
16401                             // look for drop interactions
16402                             if (isDrop) {
16403                                 dropEvts.push( oDD );
16404                             // look for drag enter and drag over interactions
16405                             } else {
16406
16407                                 // initial drag over: dragEnter fires
16408                                 if (!oldOvers[oDD.id]) {
16409                                     enterEvts.push( oDD );
16410                                 // subsequent drag overs: dragOver fires
16411                                 } else {
16412                                     overEvts.push( oDD );
16413                                 }
16414
16415                                 this.dragOvers[oDD.id] = oDD;
16416                             }
16417                         }
16418                     }
16419                 }
16420             }
16421
16422             if (this.mode) {
16423                 if (outEvts.length) {
16424                     dc.b4DragOut(e, outEvts);
16425                     dc.onDragOut(e, outEvts);
16426                 }
16427
16428                 if (enterEvts.length) {
16429                     dc.onDragEnter(e, enterEvts);
16430                 }
16431
16432                 if (overEvts.length) {
16433                     dc.b4DragOver(e, overEvts);
16434                     dc.onDragOver(e, overEvts);
16435                 }
16436
16437                 if (dropEvts.length) {
16438                     dc.b4DragDrop(e, dropEvts);
16439                     dc.onDragDrop(e, dropEvts);
16440                 }
16441
16442             } else {
16443                 // fire dragout events
16444                 var len = 0;
16445                 for (i=0, len=outEvts.length; i<len; ++i) {
16446                     dc.b4DragOut(e, outEvts[i].id);
16447                     dc.onDragOut(e, outEvts[i].id);
16448                 }
16449
16450                 // fire enter events
16451                 for (i=0,len=enterEvts.length; i<len; ++i) {
16452                     // dc.b4DragEnter(e, oDD.id);
16453                     dc.onDragEnter(e, enterEvts[i].id);
16454                 }
16455
16456                 // fire over events
16457                 for (i=0,len=overEvts.length; i<len; ++i) {
16458                     dc.b4DragOver(e, overEvts[i].id);
16459                     dc.onDragOver(e, overEvts[i].id);
16460                 }
16461
16462                 // fire drop events
16463                 for (i=0, len=dropEvts.length; i<len; ++i) {
16464                     dc.b4DragDrop(e, dropEvts[i].id);
16465                     dc.onDragDrop(e, dropEvts[i].id);
16466                 }
16467
16468             }
16469
16470             // notify about a drop that did not find a target
16471             if (isDrop && !dropEvts.length) {
16472                 dc.onInvalidDrop(e);
16473             }
16474
16475         },
16476
16477         /**
16478          * Helper function for getting the best match from the list of drag
16479          * and drop objects returned by the drag and drop events when we are
16480          * in INTERSECT mode.  It returns either the first object that the
16481          * cursor is over, or the object that has the greatest overlap with
16482          * the dragged element.
16483          * @method getBestMatch
16484          * @param  {DragDrop[]} dds The array of drag and drop objects
16485          * targeted
16486          * @return {DragDrop}       The best single match
16487          * @static
16488          */
16489         getBestMatch: function(dds) {
16490             var winner = null;
16491             // Return null if the input is not what we expect
16492             //if (!dds || !dds.length || dds.length == 0) {
16493                // winner = null;
16494             // If there is only one item, it wins
16495             //} else if (dds.length == 1) {
16496
16497             var len = dds.length;
16498
16499             if (len == 1) {
16500                 winner = dds[0];
16501             } else {
16502                 // Loop through the targeted items
16503                 for (var i=0; i<len; ++i) {
16504                     var dd = dds[i];
16505                     // If the cursor is over the object, it wins.  If the
16506                     // cursor is over multiple matches, the first one we come
16507                     // to wins.
16508                     if (dd.cursorIsOver) {
16509                         winner = dd;
16510                         break;
16511                     // Otherwise the object with the most overlap wins
16512                     } else {
16513                         if (!winner ||
16514                             winner.overlap.getArea() < dd.overlap.getArea()) {
16515                             winner = dd;
16516                         }
16517                     }
16518                 }
16519             }
16520
16521             return winner;
16522         },
16523
16524         /**
16525          * Refreshes the cache of the top-left and bottom-right points of the
16526          * drag and drop objects in the specified group(s).  This is in the
16527          * format that is stored in the drag and drop instance, so typical
16528          * usage is:
16529          * <code>
16530          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16531          * </code>
16532          * Alternatively:
16533          * <code>
16534          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16535          * </code>
16536          * @TODO this really should be an indexed array.  Alternatively this
16537          * method could accept both.
16538          * @method refreshCache
16539          * @param {Object} groups an associative array of groups to refresh
16540          * @static
16541          */
16542         refreshCache: function(groups) {
16543             for (var sGroup in groups) {
16544                 if ("string" != typeof sGroup) {
16545                     continue;
16546                 }
16547                 for (var i in this.ids[sGroup]) {
16548                     var oDD = this.ids[sGroup][i];
16549
16550                     if (this.isTypeOfDD(oDD)) {
16551                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16552                         var loc = this.getLocation(oDD);
16553                         if (loc) {
16554                             this.locationCache[oDD.id] = loc;
16555                         } else {
16556                             delete this.locationCache[oDD.id];
16557                             // this will unregister the drag and drop object if
16558                             // the element is not in a usable state
16559                             // oDD.unreg();
16560                         }
16561                     }
16562                 }
16563             }
16564         },
16565
16566         /**
16567          * This checks to make sure an element exists and is in the DOM.  The
16568          * main purpose is to handle cases where innerHTML is used to remove
16569          * drag and drop objects from the DOM.  IE provides an 'unspecified
16570          * error' when trying to access the offsetParent of such an element
16571          * @method verifyEl
16572          * @param {HTMLElement} el the element to check
16573          * @return {boolean} true if the element looks usable
16574          * @static
16575          */
16576         verifyEl: function(el) {
16577             if (el) {
16578                 var parent;
16579                 if(Roo.isIE){
16580                     try{
16581                         parent = el.offsetParent;
16582                     }catch(e){}
16583                 }else{
16584                     parent = el.offsetParent;
16585                 }
16586                 if (parent) {
16587                     return true;
16588                 }
16589             }
16590
16591             return false;
16592         },
16593
16594         /**
16595          * Returns a Region object containing the drag and drop element's position
16596          * and size, including the padding configured for it
16597          * @method getLocation
16598          * @param {DragDrop} oDD the drag and drop object to get the
16599          *                       location for
16600          * @return {Roo.lib.Region} a Region object representing the total area
16601          *                             the element occupies, including any padding
16602          *                             the instance is configured for.
16603          * @static
16604          */
16605         getLocation: function(oDD) {
16606             if (! this.isTypeOfDD(oDD)) {
16607                 return null;
16608             }
16609
16610             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16611
16612             try {
16613                 pos= Roo.lib.Dom.getXY(el);
16614             } catch (e) { }
16615
16616             if (!pos) {
16617                 return null;
16618             }
16619
16620             x1 = pos[0];
16621             x2 = x1 + el.offsetWidth;
16622             y1 = pos[1];
16623             y2 = y1 + el.offsetHeight;
16624
16625             t = y1 - oDD.padding[0];
16626             r = x2 + oDD.padding[1];
16627             b = y2 + oDD.padding[2];
16628             l = x1 - oDD.padding[3];
16629
16630             return new Roo.lib.Region( t, r, b, l );
16631         },
16632
16633         /**
16634          * Checks the cursor location to see if it over the target
16635          * @method isOverTarget
16636          * @param {Roo.lib.Point} pt The point to evaluate
16637          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16638          * @return {boolean} true if the mouse is over the target
16639          * @private
16640          * @static
16641          */
16642         isOverTarget: function(pt, oTarget, intersect) {
16643             // use cache if available
16644             var loc = this.locationCache[oTarget.id];
16645             if (!loc || !this.useCache) {
16646                 loc = this.getLocation(oTarget);
16647                 this.locationCache[oTarget.id] = loc;
16648
16649             }
16650
16651             if (!loc) {
16652                 return false;
16653             }
16654
16655             oTarget.cursorIsOver = loc.contains( pt );
16656
16657             // DragDrop is using this as a sanity check for the initial mousedown
16658             // in this case we are done.  In POINT mode, if the drag obj has no
16659             // contraints, we are also done. Otherwise we need to evaluate the
16660             // location of the target as related to the actual location of the
16661             // dragged element.
16662             var dc = this.dragCurrent;
16663             if (!dc || !dc.getTargetCoord ||
16664                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16665                 return oTarget.cursorIsOver;
16666             }
16667
16668             oTarget.overlap = null;
16669
16670             // Get the current location of the drag element, this is the
16671             // location of the mouse event less the delta that represents
16672             // where the original mousedown happened on the element.  We
16673             // need to consider constraints and ticks as well.
16674             var pos = dc.getTargetCoord(pt.x, pt.y);
16675
16676             var el = dc.getDragEl();
16677             var curRegion = new Roo.lib.Region( pos.y,
16678                                                    pos.x + el.offsetWidth,
16679                                                    pos.y + el.offsetHeight,
16680                                                    pos.x );
16681
16682             var overlap = curRegion.intersect(loc);
16683
16684             if (overlap) {
16685                 oTarget.overlap = overlap;
16686                 return (intersect) ? true : oTarget.cursorIsOver;
16687             } else {
16688                 return false;
16689             }
16690         },
16691
16692         /**
16693          * unload event handler
16694          * @method _onUnload
16695          * @private
16696          * @static
16697          */
16698         _onUnload: function(e, me) {
16699             Roo.dd.DragDropMgr.unregAll();
16700         },
16701
16702         /**
16703          * Cleans up the drag and drop events and objects.
16704          * @method unregAll
16705          * @private
16706          * @static
16707          */
16708         unregAll: function() {
16709
16710             if (this.dragCurrent) {
16711                 this.stopDrag();
16712                 this.dragCurrent = null;
16713             }
16714
16715             this._execOnAll("unreg", []);
16716
16717             for (i in this.elementCache) {
16718                 delete this.elementCache[i];
16719             }
16720
16721             this.elementCache = {};
16722             this.ids = {};
16723         },
16724
16725         /**
16726          * A cache of DOM elements
16727          * @property elementCache
16728          * @private
16729          * @static
16730          */
16731         elementCache: {},
16732
16733         /**
16734          * Get the wrapper for the DOM element specified
16735          * @method getElWrapper
16736          * @param {String} id the id of the element to get
16737          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16738          * @private
16739          * @deprecated This wrapper isn't that useful
16740          * @static
16741          */
16742         getElWrapper: function(id) {
16743             var oWrapper = this.elementCache[id];
16744             if (!oWrapper || !oWrapper.el) {
16745                 oWrapper = this.elementCache[id] =
16746                     new this.ElementWrapper(Roo.getDom(id));
16747             }
16748             return oWrapper;
16749         },
16750
16751         /**
16752          * Returns the actual DOM element
16753          * @method getElement
16754          * @param {String} id the id of the elment to get
16755          * @return {Object} The element
16756          * @deprecated use Roo.getDom instead
16757          * @static
16758          */
16759         getElement: function(id) {
16760             return Roo.getDom(id);
16761         },
16762
16763         /**
16764          * Returns the style property for the DOM element (i.e.,
16765          * document.getElById(id).style)
16766          * @method getCss
16767          * @param {String} id the id of the elment to get
16768          * @return {Object} The style property of the element
16769          * @deprecated use Roo.getDom instead
16770          * @static
16771          */
16772         getCss: function(id) {
16773             var el = Roo.getDom(id);
16774             return (el) ? el.style : null;
16775         },
16776
16777         /**
16778          * Inner class for cached elements
16779          * @class DragDropMgr.ElementWrapper
16780          * @for DragDropMgr
16781          * @private
16782          * @deprecated
16783          */
16784         ElementWrapper: function(el) {
16785                 /**
16786                  * The element
16787                  * @property el
16788                  */
16789                 this.el = el || null;
16790                 /**
16791                  * The element id
16792                  * @property id
16793                  */
16794                 this.id = this.el && el.id;
16795                 /**
16796                  * A reference to the style property
16797                  * @property css
16798                  */
16799                 this.css = this.el && el.style;
16800             },
16801
16802         /**
16803          * Returns the X position of an html element
16804          * @method getPosX
16805          * @param el the element for which to get the position
16806          * @return {int} the X coordinate
16807          * @for DragDropMgr
16808          * @deprecated use Roo.lib.Dom.getX instead
16809          * @static
16810          */
16811         getPosX: function(el) {
16812             return Roo.lib.Dom.getX(el);
16813         },
16814
16815         /**
16816          * Returns the Y position of an html element
16817          * @method getPosY
16818          * @param el the element for which to get the position
16819          * @return {int} the Y coordinate
16820          * @deprecated use Roo.lib.Dom.getY instead
16821          * @static
16822          */
16823         getPosY: function(el) {
16824             return Roo.lib.Dom.getY(el);
16825         },
16826
16827         /**
16828          * Swap two nodes.  In IE, we use the native method, for others we
16829          * emulate the IE behavior
16830          * @method swapNode
16831          * @param n1 the first node to swap
16832          * @param n2 the other node to swap
16833          * @static
16834          */
16835         swapNode: function(n1, n2) {
16836             if (n1.swapNode) {
16837                 n1.swapNode(n2);
16838             } else {
16839                 var p = n2.parentNode;
16840                 var s = n2.nextSibling;
16841
16842                 if (s == n1) {
16843                     p.insertBefore(n1, n2);
16844                 } else if (n2 == n1.nextSibling) {
16845                     p.insertBefore(n2, n1);
16846                 } else {
16847                     n1.parentNode.replaceChild(n2, n1);
16848                     p.insertBefore(n1, s);
16849                 }
16850             }
16851         },
16852
16853         /**
16854          * Returns the current scroll position
16855          * @method getScroll
16856          * @private
16857          * @static
16858          */
16859         getScroll: function () {
16860             var t, l, dde=document.documentElement, db=document.body;
16861             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16862                 t = dde.scrollTop;
16863                 l = dde.scrollLeft;
16864             } else if (db) {
16865                 t = db.scrollTop;
16866                 l = db.scrollLeft;
16867             } else {
16868
16869             }
16870             return { top: t, left: l };
16871         },
16872
16873         /**
16874          * Returns the specified element style property
16875          * @method getStyle
16876          * @param {HTMLElement} el          the element
16877          * @param {string}      styleProp   the style property
16878          * @return {string} The value of the style property
16879          * @deprecated use Roo.lib.Dom.getStyle
16880          * @static
16881          */
16882         getStyle: function(el, styleProp) {
16883             return Roo.fly(el).getStyle(styleProp);
16884         },
16885
16886         /**
16887          * Gets the scrollTop
16888          * @method getScrollTop
16889          * @return {int} the document's scrollTop
16890          * @static
16891          */
16892         getScrollTop: function () { return this.getScroll().top; },
16893
16894         /**
16895          * Gets the scrollLeft
16896          * @method getScrollLeft
16897          * @return {int} the document's scrollTop
16898          * @static
16899          */
16900         getScrollLeft: function () { return this.getScroll().left; },
16901
16902         /**
16903          * Sets the x/y position of an element to the location of the
16904          * target element.
16905          * @method moveToEl
16906          * @param {HTMLElement} moveEl      The element to move
16907          * @param {HTMLElement} targetEl    The position reference element
16908          * @static
16909          */
16910         moveToEl: function (moveEl, targetEl) {
16911             var aCoord = Roo.lib.Dom.getXY(targetEl);
16912             Roo.lib.Dom.setXY(moveEl, aCoord);
16913         },
16914
16915         /**
16916          * Numeric array sort function
16917          * @method numericSort
16918          * @static
16919          */
16920         numericSort: function(a, b) { return (a - b); },
16921
16922         /**
16923          * Internal counter
16924          * @property _timeoutCount
16925          * @private
16926          * @static
16927          */
16928         _timeoutCount: 0,
16929
16930         /**
16931          * Trying to make the load order less important.  Without this we get
16932          * an error if this file is loaded before the Event Utility.
16933          * @method _addListeners
16934          * @private
16935          * @static
16936          */
16937         _addListeners: function() {
16938             var DDM = Roo.dd.DDM;
16939             if ( Roo.lib.Event && document ) {
16940                 DDM._onLoad();
16941             } else {
16942                 if (DDM._timeoutCount > 2000) {
16943                 } else {
16944                     setTimeout(DDM._addListeners, 10);
16945                     if (document && document.body) {
16946                         DDM._timeoutCount += 1;
16947                     }
16948                 }
16949             }
16950         },
16951
16952         /**
16953          * Recursively searches the immediate parent and all child nodes for
16954          * the handle element in order to determine wheter or not it was
16955          * clicked.
16956          * @method handleWasClicked
16957          * @param node the html element to inspect
16958          * @static
16959          */
16960         handleWasClicked: function(node, id) {
16961             if (this.isHandle(id, node.id)) {
16962                 return true;
16963             } else {
16964                 // check to see if this is a text node child of the one we want
16965                 var p = node.parentNode;
16966
16967                 while (p) {
16968                     if (this.isHandle(id, p.id)) {
16969                         return true;
16970                     } else {
16971                         p = p.parentNode;
16972                     }
16973                 }
16974             }
16975
16976             return false;
16977         }
16978
16979     };
16980
16981 }();
16982
16983 // shorter alias, save a few bytes
16984 Roo.dd.DDM = Roo.dd.DragDropMgr;
16985 Roo.dd.DDM._addListeners();
16986
16987 }/*
16988  * Based on:
16989  * Ext JS Library 1.1.1
16990  * Copyright(c) 2006-2007, Ext JS, LLC.
16991  *
16992  * Originally Released Under LGPL - original licence link has changed is not relivant.
16993  *
16994  * Fork - LGPL
16995  * <script type="text/javascript">
16996  */
16997
16998 /**
16999  * @class Roo.dd.DD
17000  * A DragDrop implementation where the linked element follows the
17001  * mouse cursor during a drag.
17002  * @extends Roo.dd.DragDrop
17003  * @constructor
17004  * @param {String} id the id of the linked element
17005  * @param {String} sGroup the group of related DragDrop items
17006  * @param {object} config an object containing configurable attributes
17007  *                Valid properties for DD:
17008  *                    scroll
17009  */
17010 Roo.dd.DD = function(id, sGroup, config) {
17011     if (id) {
17012         this.init(id, sGroup, config);
17013     }
17014 };
17015
17016 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17017
17018     /**
17019      * When set to true, the utility automatically tries to scroll the browser
17020      * window wehn a drag and drop element is dragged near the viewport boundary.
17021      * Defaults to true.
17022      * @property scroll
17023      * @type boolean
17024      */
17025     scroll: true,
17026
17027     /**
17028      * Sets the pointer offset to the distance between the linked element's top
17029      * left corner and the location the element was clicked
17030      * @method autoOffset
17031      * @param {int} iPageX the X coordinate of the click
17032      * @param {int} iPageY the Y coordinate of the click
17033      */
17034     autoOffset: function(iPageX, iPageY) {
17035         var x = iPageX - this.startPageX;
17036         var y = iPageY - this.startPageY;
17037         this.setDelta(x, y);
17038     },
17039
17040     /**
17041      * Sets the pointer offset.  You can call this directly to force the
17042      * offset to be in a particular location (e.g., pass in 0,0 to set it
17043      * to the center of the object)
17044      * @method setDelta
17045      * @param {int} iDeltaX the distance from the left
17046      * @param {int} iDeltaY the distance from the top
17047      */
17048     setDelta: function(iDeltaX, iDeltaY) {
17049         this.deltaX = iDeltaX;
17050         this.deltaY = iDeltaY;
17051     },
17052
17053     /**
17054      * Sets the drag element to the location of the mousedown or click event,
17055      * maintaining the cursor location relative to the location on the element
17056      * that was clicked.  Override this if you want to place the element in a
17057      * location other than where the cursor is.
17058      * @method setDragElPos
17059      * @param {int} iPageX the X coordinate of the mousedown or drag event
17060      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17061      */
17062     setDragElPos: function(iPageX, iPageY) {
17063         // the first time we do this, we are going to check to make sure
17064         // the element has css positioning
17065
17066         var el = this.getDragEl();
17067         this.alignElWithMouse(el, iPageX, iPageY);
17068     },
17069
17070     /**
17071      * Sets the element to the location of the mousedown or click event,
17072      * maintaining the cursor location relative to the location on the element
17073      * that was clicked.  Override this if you want to place the element in a
17074      * location other than where the cursor is.
17075      * @method alignElWithMouse
17076      * @param {HTMLElement} el the element to move
17077      * @param {int} iPageX the X coordinate of the mousedown or drag event
17078      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17079      */
17080     alignElWithMouse: function(el, iPageX, iPageY) {
17081         var oCoord = this.getTargetCoord(iPageX, iPageY);
17082         var fly = el.dom ? el : Roo.fly(el);
17083         if (!this.deltaSetXY) {
17084             var aCoord = [oCoord.x, oCoord.y];
17085             fly.setXY(aCoord);
17086             var newLeft = fly.getLeft(true);
17087             var newTop  = fly.getTop(true);
17088             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17089         } else {
17090             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17091         }
17092
17093         this.cachePosition(oCoord.x, oCoord.y);
17094         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17095         return oCoord;
17096     },
17097
17098     /**
17099      * Saves the most recent position so that we can reset the constraints and
17100      * tick marks on-demand.  We need to know this so that we can calculate the
17101      * number of pixels the element is offset from its original position.
17102      * @method cachePosition
17103      * @param iPageX the current x position (optional, this just makes it so we
17104      * don't have to look it up again)
17105      * @param iPageY the current y position (optional, this just makes it so we
17106      * don't have to look it up again)
17107      */
17108     cachePosition: function(iPageX, iPageY) {
17109         if (iPageX) {
17110             this.lastPageX = iPageX;
17111             this.lastPageY = iPageY;
17112         } else {
17113             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17114             this.lastPageX = aCoord[0];
17115             this.lastPageY = aCoord[1];
17116         }
17117     },
17118
17119     /**
17120      * Auto-scroll the window if the dragged object has been moved beyond the
17121      * visible window boundary.
17122      * @method autoScroll
17123      * @param {int} x the drag element's x position
17124      * @param {int} y the drag element's y position
17125      * @param {int} h the height of the drag element
17126      * @param {int} w the width of the drag element
17127      * @private
17128      */
17129     autoScroll: function(x, y, h, w) {
17130
17131         if (this.scroll) {
17132             // The client height
17133             var clientH = Roo.lib.Dom.getViewWidth();
17134
17135             // The client width
17136             var clientW = Roo.lib.Dom.getViewHeight();
17137
17138             // The amt scrolled down
17139             var st = this.DDM.getScrollTop();
17140
17141             // The amt scrolled right
17142             var sl = this.DDM.getScrollLeft();
17143
17144             // Location of the bottom of the element
17145             var bot = h + y;
17146
17147             // Location of the right of the element
17148             var right = w + x;
17149
17150             // The distance from the cursor to the bottom of the visible area,
17151             // adjusted so that we don't scroll if the cursor is beyond the
17152             // element drag constraints
17153             var toBot = (clientH + st - y - this.deltaY);
17154
17155             // The distance from the cursor to the right of the visible area
17156             var toRight = (clientW + sl - x - this.deltaX);
17157
17158
17159             // How close to the edge the cursor must be before we scroll
17160             // var thresh = (document.all) ? 100 : 40;
17161             var thresh = 40;
17162
17163             // How many pixels to scroll per autoscroll op.  This helps to reduce
17164             // clunky scrolling. IE is more sensitive about this ... it needs this
17165             // value to be higher.
17166             var scrAmt = (document.all) ? 80 : 30;
17167
17168             // Scroll down if we are near the bottom of the visible page and the
17169             // obj extends below the crease
17170             if ( bot > clientH && toBot < thresh ) {
17171                 window.scrollTo(sl, st + scrAmt);
17172             }
17173
17174             // Scroll up if the window is scrolled down and the top of the object
17175             // goes above the top border
17176             if ( y < st && st > 0 && y - st < thresh ) {
17177                 window.scrollTo(sl, st - scrAmt);
17178             }
17179
17180             // Scroll right if the obj is beyond the right border and the cursor is
17181             // near the border.
17182             if ( right > clientW && toRight < thresh ) {
17183                 window.scrollTo(sl + scrAmt, st);
17184             }
17185
17186             // Scroll left if the window has been scrolled to the right and the obj
17187             // extends past the left border
17188             if ( x < sl && sl > 0 && x - sl < thresh ) {
17189                 window.scrollTo(sl - scrAmt, st);
17190             }
17191         }
17192     },
17193
17194     /**
17195      * Finds the location the element should be placed if we want to move
17196      * it to where the mouse location less the click offset would place us.
17197      * @method getTargetCoord
17198      * @param {int} iPageX the X coordinate of the click
17199      * @param {int} iPageY the Y coordinate of the click
17200      * @return an object that contains the coordinates (Object.x and Object.y)
17201      * @private
17202      */
17203     getTargetCoord: function(iPageX, iPageY) {
17204
17205
17206         var x = iPageX - this.deltaX;
17207         var y = iPageY - this.deltaY;
17208
17209         if (this.constrainX) {
17210             if (x < this.minX) { x = this.minX; }
17211             if (x > this.maxX) { x = this.maxX; }
17212         }
17213
17214         if (this.constrainY) {
17215             if (y < this.minY) { y = this.minY; }
17216             if (y > this.maxY) { y = this.maxY; }
17217         }
17218
17219         x = this.getTick(x, this.xTicks);
17220         y = this.getTick(y, this.yTicks);
17221
17222
17223         return {x:x, y:y};
17224     },
17225
17226     /*
17227      * Sets up config options specific to this class. Overrides
17228      * Roo.dd.DragDrop, but all versions of this method through the
17229      * inheritance chain are called
17230      */
17231     applyConfig: function() {
17232         Roo.dd.DD.superclass.applyConfig.call(this);
17233         this.scroll = (this.config.scroll !== false);
17234     },
17235
17236     /*
17237      * Event that fires prior to the onMouseDown event.  Overrides
17238      * Roo.dd.DragDrop.
17239      */
17240     b4MouseDown: function(e) {
17241         // this.resetConstraints();
17242         this.autoOffset(e.getPageX(),
17243                             e.getPageY());
17244     },
17245
17246     /*
17247      * Event that fires prior to the onDrag event.  Overrides
17248      * Roo.dd.DragDrop.
17249      */
17250     b4Drag: function(e) {
17251         this.setDragElPos(e.getPageX(),
17252                             e.getPageY());
17253     },
17254
17255     toString: function() {
17256         return ("DD " + this.id);
17257     }
17258
17259     //////////////////////////////////////////////////////////////////////////
17260     // Debugging ygDragDrop events that can be overridden
17261     //////////////////////////////////////////////////////////////////////////
17262     /*
17263     startDrag: function(x, y) {
17264     },
17265
17266     onDrag: function(e) {
17267     },
17268
17269     onDragEnter: function(e, id) {
17270     },
17271
17272     onDragOver: function(e, id) {
17273     },
17274
17275     onDragOut: function(e, id) {
17276     },
17277
17278     onDragDrop: function(e, id) {
17279     },
17280
17281     endDrag: function(e) {
17282     }
17283
17284     */
17285
17286 });/*
17287  * Based on:
17288  * Ext JS Library 1.1.1
17289  * Copyright(c) 2006-2007, Ext JS, LLC.
17290  *
17291  * Originally Released Under LGPL - original licence link has changed is not relivant.
17292  *
17293  * Fork - LGPL
17294  * <script type="text/javascript">
17295  */
17296
17297 /**
17298  * @class Roo.dd.DDProxy
17299  * A DragDrop implementation that inserts an empty, bordered div into
17300  * the document that follows the cursor during drag operations.  At the time of
17301  * the click, the frame div is resized to the dimensions of the linked html
17302  * element, and moved to the exact location of the linked element.
17303  *
17304  * References to the "frame" element refer to the single proxy element that
17305  * was created to be dragged in place of all DDProxy elements on the
17306  * page.
17307  *
17308  * @extends Roo.dd.DD
17309  * @constructor
17310  * @param {String} id the id of the linked html element
17311  * @param {String} sGroup the group of related DragDrop objects
17312  * @param {object} config an object containing configurable attributes
17313  *                Valid properties for DDProxy in addition to those in DragDrop:
17314  *                   resizeFrame, centerFrame, dragElId
17315  */
17316 Roo.dd.DDProxy = function(id, sGroup, config) {
17317     if (id) {
17318         this.init(id, sGroup, config);
17319         this.initFrame();
17320     }
17321 };
17322
17323 /**
17324  * The default drag frame div id
17325  * @property Roo.dd.DDProxy.dragElId
17326  * @type String
17327  * @static
17328  */
17329 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17330
17331 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17332
17333     /**
17334      * By default we resize the drag frame to be the same size as the element
17335      * we want to drag (this is to get the frame effect).  We can turn it off
17336      * if we want a different behavior.
17337      * @property resizeFrame
17338      * @type boolean
17339      */
17340     resizeFrame: true,
17341
17342     /**
17343      * By default the frame is positioned exactly where the drag element is, so
17344      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17345      * you do not have constraints on the obj is to have the drag frame centered
17346      * around the cursor.  Set centerFrame to true for this effect.
17347      * @property centerFrame
17348      * @type boolean
17349      */
17350     centerFrame: false,
17351
17352     /**
17353      * Creates the proxy element if it does not yet exist
17354      * @method createFrame
17355      */
17356     createFrame: function() {
17357         var self = this;
17358         var body = document.body;
17359
17360         if (!body || !body.firstChild) {
17361             setTimeout( function() { self.createFrame(); }, 50 );
17362             return;
17363         }
17364
17365         var div = this.getDragEl();
17366
17367         if (!div) {
17368             div    = document.createElement("div");
17369             div.id = this.dragElId;
17370             var s  = div.style;
17371
17372             s.position   = "absolute";
17373             s.visibility = "hidden";
17374             s.cursor     = "move";
17375             s.border     = "2px solid #aaa";
17376             s.zIndex     = 999;
17377
17378             // appendChild can blow up IE if invoked prior to the window load event
17379             // while rendering a table.  It is possible there are other scenarios
17380             // that would cause this to happen as well.
17381             body.insertBefore(div, body.firstChild);
17382         }
17383     },
17384
17385     /**
17386      * Initialization for the drag frame element.  Must be called in the
17387      * constructor of all subclasses
17388      * @method initFrame
17389      */
17390     initFrame: function() {
17391         this.createFrame();
17392     },
17393
17394     applyConfig: function() {
17395         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17396
17397         this.resizeFrame = (this.config.resizeFrame !== false);
17398         this.centerFrame = (this.config.centerFrame);
17399         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17400     },
17401
17402     /**
17403      * Resizes the drag frame to the dimensions of the clicked object, positions
17404      * it over the object, and finally displays it
17405      * @method showFrame
17406      * @param {int} iPageX X click position
17407      * @param {int} iPageY Y click position
17408      * @private
17409      */
17410     showFrame: function(iPageX, iPageY) {
17411         var el = this.getEl();
17412         var dragEl = this.getDragEl();
17413         var s = dragEl.style;
17414
17415         this._resizeProxy();
17416
17417         if (this.centerFrame) {
17418             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17419                            Math.round(parseInt(s.height, 10)/2) );
17420         }
17421
17422         this.setDragElPos(iPageX, iPageY);
17423
17424         Roo.fly(dragEl).show();
17425     },
17426
17427     /**
17428      * The proxy is automatically resized to the dimensions of the linked
17429      * element when a drag is initiated, unless resizeFrame is set to false
17430      * @method _resizeProxy
17431      * @private
17432      */
17433     _resizeProxy: function() {
17434         if (this.resizeFrame) {
17435             var el = this.getEl();
17436             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17437         }
17438     },
17439
17440     // overrides Roo.dd.DragDrop
17441     b4MouseDown: function(e) {
17442         var x = e.getPageX();
17443         var y = e.getPageY();
17444         this.autoOffset(x, y);
17445         this.setDragElPos(x, y);
17446     },
17447
17448     // overrides Roo.dd.DragDrop
17449     b4StartDrag: function(x, y) {
17450         // show the drag frame
17451         this.showFrame(x, y);
17452     },
17453
17454     // overrides Roo.dd.DragDrop
17455     b4EndDrag: function(e) {
17456         Roo.fly(this.getDragEl()).hide();
17457     },
17458
17459     // overrides Roo.dd.DragDrop
17460     // By default we try to move the element to the last location of the frame.
17461     // This is so that the default behavior mirrors that of Roo.dd.DD.
17462     endDrag: function(e) {
17463
17464         var lel = this.getEl();
17465         var del = this.getDragEl();
17466
17467         // Show the drag frame briefly so we can get its position
17468         del.style.visibility = "";
17469
17470         this.beforeMove();
17471         // Hide the linked element before the move to get around a Safari
17472         // rendering bug.
17473         lel.style.visibility = "hidden";
17474         Roo.dd.DDM.moveToEl(lel, del);
17475         del.style.visibility = "hidden";
17476         lel.style.visibility = "";
17477
17478         this.afterDrag();
17479     },
17480
17481     beforeMove : function(){
17482
17483     },
17484
17485     afterDrag : function(){
17486
17487     },
17488
17489     toString: function() {
17490         return ("DDProxy " + this.id);
17491     }
17492
17493 });
17494 /*
17495  * Based on:
17496  * Ext JS Library 1.1.1
17497  * Copyright(c) 2006-2007, Ext JS, LLC.
17498  *
17499  * Originally Released Under LGPL - original licence link has changed is not relivant.
17500  *
17501  * Fork - LGPL
17502  * <script type="text/javascript">
17503  */
17504
17505  /**
17506  * @class Roo.dd.DDTarget
17507  * A DragDrop implementation that does not move, but can be a drop
17508  * target.  You would get the same result by simply omitting implementation
17509  * for the event callbacks, but this way we reduce the processing cost of the
17510  * event listener and the callbacks.
17511  * @extends Roo.dd.DragDrop
17512  * @constructor
17513  * @param {String} id the id of the element that is a drop target
17514  * @param {String} sGroup the group of related DragDrop objects
17515  * @param {object} config an object containing configurable attributes
17516  *                 Valid properties for DDTarget in addition to those in
17517  *                 DragDrop:
17518  *                    none
17519  */
17520 Roo.dd.DDTarget = function(id, sGroup, config) {
17521     if (id) {
17522         this.initTarget(id, sGroup, config);
17523     }
17524     if (config.listeners || config.events) { 
17525        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17526             listeners : config.listeners || {}, 
17527             events : config.events || {} 
17528         });    
17529     }
17530 };
17531
17532 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17533 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17534     toString: function() {
17535         return ("DDTarget " + this.id);
17536     }
17537 });
17538 /*
17539  * Based on:
17540  * Ext JS Library 1.1.1
17541  * Copyright(c) 2006-2007, Ext JS, LLC.
17542  *
17543  * Originally Released Under LGPL - original licence link has changed is not relivant.
17544  *
17545  * Fork - LGPL
17546  * <script type="text/javascript">
17547  */
17548  
17549
17550 /**
17551  * @class Roo.dd.ScrollManager
17552  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17553  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17554  * @singleton
17555  */
17556 Roo.dd.ScrollManager = function(){
17557     var ddm = Roo.dd.DragDropMgr;
17558     var els = {};
17559     var dragEl = null;
17560     var proc = {};
17561     
17562     var onStop = function(e){
17563         dragEl = null;
17564         clearProc();
17565     };
17566     
17567     var triggerRefresh = function(){
17568         if(ddm.dragCurrent){
17569              ddm.refreshCache(ddm.dragCurrent.groups);
17570         }
17571     };
17572     
17573     var doScroll = function(){
17574         if(ddm.dragCurrent){
17575             var dds = Roo.dd.ScrollManager;
17576             if(!dds.animate){
17577                 if(proc.el.scroll(proc.dir, dds.increment)){
17578                     triggerRefresh();
17579                 }
17580             }else{
17581                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17582             }
17583         }
17584     };
17585     
17586     var clearProc = function(){
17587         if(proc.id){
17588             clearInterval(proc.id);
17589         }
17590         proc.id = 0;
17591         proc.el = null;
17592         proc.dir = "";
17593     };
17594     
17595     var startProc = function(el, dir){
17596         clearProc();
17597         proc.el = el;
17598         proc.dir = dir;
17599         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17600     };
17601     
17602     var onFire = function(e, isDrop){
17603         if(isDrop || !ddm.dragCurrent){ return; }
17604         var dds = Roo.dd.ScrollManager;
17605         if(!dragEl || dragEl != ddm.dragCurrent){
17606             dragEl = ddm.dragCurrent;
17607             // refresh regions on drag start
17608             dds.refreshCache();
17609         }
17610         
17611         var xy = Roo.lib.Event.getXY(e);
17612         var pt = new Roo.lib.Point(xy[0], xy[1]);
17613         for(var id in els){
17614             var el = els[id], r = el._region;
17615             if(r && r.contains(pt) && el.isScrollable()){
17616                 if(r.bottom - pt.y <= dds.thresh){
17617                     if(proc.el != el){
17618                         startProc(el, "down");
17619                     }
17620                     return;
17621                 }else if(r.right - pt.x <= dds.thresh){
17622                     if(proc.el != el){
17623                         startProc(el, "left");
17624                     }
17625                     return;
17626                 }else if(pt.y - r.top <= dds.thresh){
17627                     if(proc.el != el){
17628                         startProc(el, "up");
17629                     }
17630                     return;
17631                 }else if(pt.x - r.left <= dds.thresh){
17632                     if(proc.el != el){
17633                         startProc(el, "right");
17634                     }
17635                     return;
17636                 }
17637             }
17638         }
17639         clearProc();
17640     };
17641     
17642     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17643     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17644     
17645     return {
17646         /**
17647          * Registers new overflow element(s) to auto scroll
17648          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17649          */
17650         register : function(el){
17651             if(el instanceof Array){
17652                 for(var i = 0, len = el.length; i < len; i++) {
17653                         this.register(el[i]);
17654                 }
17655             }else{
17656                 el = Roo.get(el);
17657                 els[el.id] = el;
17658             }
17659         },
17660         
17661         /**
17662          * Unregisters overflow element(s) so they are no longer scrolled
17663          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17664          */
17665         unregister : function(el){
17666             if(el instanceof Array){
17667                 for(var i = 0, len = el.length; i < len; i++) {
17668                         this.unregister(el[i]);
17669                 }
17670             }else{
17671                 el = Roo.get(el);
17672                 delete els[el.id];
17673             }
17674         },
17675         
17676         /**
17677          * The number of pixels from the edge of a container the pointer needs to be to 
17678          * trigger scrolling (defaults to 25)
17679          * @type Number
17680          */
17681         thresh : 25,
17682         
17683         /**
17684          * The number of pixels to scroll in each scroll increment (defaults to 50)
17685          * @type Number
17686          */
17687         increment : 100,
17688         
17689         /**
17690          * The frequency of scrolls in milliseconds (defaults to 500)
17691          * @type Number
17692          */
17693         frequency : 500,
17694         
17695         /**
17696          * True to animate the scroll (defaults to true)
17697          * @type Boolean
17698          */
17699         animate: true,
17700         
17701         /**
17702          * The animation duration in seconds - 
17703          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17704          * @type Number
17705          */
17706         animDuration: .4,
17707         
17708         /**
17709          * Manually trigger a cache refresh.
17710          */
17711         refreshCache : function(){
17712             for(var id in els){
17713                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17714                     els[id]._region = els[id].getRegion();
17715                 }
17716             }
17717         }
17718     };
17719 }();/*
17720  * Based on:
17721  * Ext JS Library 1.1.1
17722  * Copyright(c) 2006-2007, Ext JS, LLC.
17723  *
17724  * Originally Released Under LGPL - original licence link has changed is not relivant.
17725  *
17726  * Fork - LGPL
17727  * <script type="text/javascript">
17728  */
17729  
17730
17731 /**
17732  * @class Roo.dd.Registry
17733  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17734  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17735  * @singleton
17736  */
17737 Roo.dd.Registry = function(){
17738     var elements = {}; 
17739     var handles = {}; 
17740     var autoIdSeed = 0;
17741
17742     var getId = function(el, autogen){
17743         if(typeof el == "string"){
17744             return el;
17745         }
17746         var id = el.id;
17747         if(!id && autogen !== false){
17748             id = "roodd-" + (++autoIdSeed);
17749             el.id = id;
17750         }
17751         return id;
17752     };
17753     
17754     return {
17755     /**
17756      * Register a drag drop element
17757      * @param {String|HTMLElement} element The id or DOM node to register
17758      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17759      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17760      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17761      * populated in the data object (if applicable):
17762      * <pre>
17763 Value      Description<br />
17764 ---------  ------------------------------------------<br />
17765 handles    Array of DOM nodes that trigger dragging<br />
17766            for the element being registered<br />
17767 isHandle   True if the element passed in triggers<br />
17768            dragging itself, else false
17769 </pre>
17770      */
17771         register : function(el, data){
17772             data = data || {};
17773             if(typeof el == "string"){
17774                 el = document.getElementById(el);
17775             }
17776             data.ddel = el;
17777             elements[getId(el)] = data;
17778             if(data.isHandle !== false){
17779                 handles[data.ddel.id] = data;
17780             }
17781             if(data.handles){
17782                 var hs = data.handles;
17783                 for(var i = 0, len = hs.length; i < len; i++){
17784                         handles[getId(hs[i])] = data;
17785                 }
17786             }
17787         },
17788
17789     /**
17790      * Unregister a drag drop element
17791      * @param {String|HTMLElement}  element The id or DOM node to unregister
17792      */
17793         unregister : function(el){
17794             var id = getId(el, false);
17795             var data = elements[id];
17796             if(data){
17797                 delete elements[id];
17798                 if(data.handles){
17799                     var hs = data.handles;
17800                     for(var i = 0, len = hs.length; i < len; i++){
17801                         delete handles[getId(hs[i], false)];
17802                     }
17803                 }
17804             }
17805         },
17806
17807     /**
17808      * Returns the handle registered for a DOM Node by id
17809      * @param {String|HTMLElement} id The DOM node or id to look up
17810      * @return {Object} handle The custom handle data
17811      */
17812         getHandle : function(id){
17813             if(typeof id != "string"){ // must be element?
17814                 id = id.id;
17815             }
17816             return handles[id];
17817         },
17818
17819     /**
17820      * Returns the handle that is registered for the DOM node that is the target of the event
17821      * @param {Event} e The event
17822      * @return {Object} handle The custom handle data
17823      */
17824         getHandleFromEvent : function(e){
17825             var t = Roo.lib.Event.getTarget(e);
17826             return t ? handles[t.id] : null;
17827         },
17828
17829     /**
17830      * Returns a custom data object that is registered for a DOM node by id
17831      * @param {String|HTMLElement} id The DOM node or id to look up
17832      * @return {Object} data The custom data
17833      */
17834         getTarget : function(id){
17835             if(typeof id != "string"){ // must be element?
17836                 id = id.id;
17837             }
17838             return elements[id];
17839         },
17840
17841     /**
17842      * Returns a custom data object that is registered for the DOM node that is the target of the event
17843      * @param {Event} e The event
17844      * @return {Object} data The custom data
17845      */
17846         getTargetFromEvent : function(e){
17847             var t = Roo.lib.Event.getTarget(e);
17848             return t ? elements[t.id] || handles[t.id] : null;
17849         }
17850     };
17851 }();/*
17852  * Based on:
17853  * Ext JS Library 1.1.1
17854  * Copyright(c) 2006-2007, Ext JS, LLC.
17855  *
17856  * Originally Released Under LGPL - original licence link has changed is not relivant.
17857  *
17858  * Fork - LGPL
17859  * <script type="text/javascript">
17860  */
17861  
17862
17863 /**
17864  * @class Roo.dd.StatusProxy
17865  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17866  * default drag proxy used by all Roo.dd components.
17867  * @constructor
17868  * @param {Object} config
17869  */
17870 Roo.dd.StatusProxy = function(config){
17871     Roo.apply(this, config);
17872     this.id = this.id || Roo.id();
17873     this.el = new Roo.Layer({
17874         dh: {
17875             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17876                 {tag: "div", cls: "x-dd-drop-icon"},
17877                 {tag: "div", cls: "x-dd-drag-ghost"}
17878             ]
17879         }, 
17880         shadow: !config || config.shadow !== false
17881     });
17882     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17883     this.dropStatus = this.dropNotAllowed;
17884 };
17885
17886 Roo.dd.StatusProxy.prototype = {
17887     /**
17888      * @cfg {String} dropAllowed
17889      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17890      */
17891     dropAllowed : "x-dd-drop-ok",
17892     /**
17893      * @cfg {String} dropNotAllowed
17894      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17895      */
17896     dropNotAllowed : "x-dd-drop-nodrop",
17897
17898     /**
17899      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17900      * over the current target element.
17901      * @param {String} cssClass The css class for the new drop status indicator image
17902      */
17903     setStatus : function(cssClass){
17904         cssClass = cssClass || this.dropNotAllowed;
17905         if(this.dropStatus != cssClass){
17906             this.el.replaceClass(this.dropStatus, cssClass);
17907             this.dropStatus = cssClass;
17908         }
17909     },
17910
17911     /**
17912      * Resets the status indicator to the default dropNotAllowed value
17913      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17914      */
17915     reset : function(clearGhost){
17916         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17917         this.dropStatus = this.dropNotAllowed;
17918         if(clearGhost){
17919             this.ghost.update("");
17920         }
17921     },
17922
17923     /**
17924      * Updates the contents of the ghost element
17925      * @param {String} html The html that will replace the current innerHTML of the ghost element
17926      */
17927     update : function(html){
17928         if(typeof html == "string"){
17929             this.ghost.update(html);
17930         }else{
17931             this.ghost.update("");
17932             html.style.margin = "0";
17933             this.ghost.dom.appendChild(html);
17934         }
17935         // ensure float = none set?? cant remember why though.
17936         var el = this.ghost.dom.firstChild;
17937                 if(el){
17938                         Roo.fly(el).setStyle('float', 'none');
17939                 }
17940     },
17941     
17942     /**
17943      * Returns the underlying proxy {@link Roo.Layer}
17944      * @return {Roo.Layer} el
17945     */
17946     getEl : function(){
17947         return this.el;
17948     },
17949
17950     /**
17951      * Returns the ghost element
17952      * @return {Roo.Element} el
17953      */
17954     getGhost : function(){
17955         return this.ghost;
17956     },
17957
17958     /**
17959      * Hides the proxy
17960      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17961      */
17962     hide : function(clear){
17963         this.el.hide();
17964         if(clear){
17965             this.reset(true);
17966         }
17967     },
17968
17969     /**
17970      * Stops the repair animation if it's currently running
17971      */
17972     stop : function(){
17973         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17974             this.anim.stop();
17975         }
17976     },
17977
17978     /**
17979      * Displays this proxy
17980      */
17981     show : function(){
17982         this.el.show();
17983     },
17984
17985     /**
17986      * Force the Layer to sync its shadow and shim positions to the element
17987      */
17988     sync : function(){
17989         this.el.sync();
17990     },
17991
17992     /**
17993      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17994      * invalid drop operation by the item being dragged.
17995      * @param {Array} xy The XY position of the element ([x, y])
17996      * @param {Function} callback The function to call after the repair is complete
17997      * @param {Object} scope The scope in which to execute the callback
17998      */
17999     repair : function(xy, callback, scope){
18000         this.callback = callback;
18001         this.scope = scope;
18002         if(xy && this.animRepair !== false){
18003             this.el.addClass("x-dd-drag-repair");
18004             this.el.hideUnders(true);
18005             this.anim = this.el.shift({
18006                 duration: this.repairDuration || .5,
18007                 easing: 'easeOut',
18008                 xy: xy,
18009                 stopFx: true,
18010                 callback: this.afterRepair,
18011                 scope: this
18012             });
18013         }else{
18014             this.afterRepair();
18015         }
18016     },
18017
18018     // private
18019     afterRepair : function(){
18020         this.hide(true);
18021         if(typeof this.callback == "function"){
18022             this.callback.call(this.scope || this);
18023         }
18024         this.callback = null;
18025         this.scope = null;
18026     }
18027 };/*
18028  * Based on:
18029  * Ext JS Library 1.1.1
18030  * Copyright(c) 2006-2007, Ext JS, LLC.
18031  *
18032  * Originally Released Under LGPL - original licence link has changed is not relivant.
18033  *
18034  * Fork - LGPL
18035  * <script type="text/javascript">
18036  */
18037
18038 /**
18039  * @class Roo.dd.DragSource
18040  * @extends Roo.dd.DDProxy
18041  * A simple class that provides the basic implementation needed to make any element draggable.
18042  * @constructor
18043  * @param {String/HTMLElement/Element} el The container element
18044  * @param {Object} config
18045  */
18046 Roo.dd.DragSource = function(el, config){
18047     this.el = Roo.get(el);
18048     this.dragData = {};
18049     
18050     Roo.apply(this, config);
18051     
18052     if(!this.proxy){
18053         this.proxy = new Roo.dd.StatusProxy();
18054     }
18055
18056     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18057           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18058     
18059     this.dragging = false;
18060 };
18061
18062 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18063     /**
18064      * @cfg {String} dropAllowed
18065      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18066      */
18067     dropAllowed : "x-dd-drop-ok",
18068     /**
18069      * @cfg {String} dropNotAllowed
18070      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18071      */
18072     dropNotAllowed : "x-dd-drop-nodrop",
18073
18074     /**
18075      * Returns the data object associated with this drag source
18076      * @return {Object} data An object containing arbitrary data
18077      */
18078     getDragData : function(e){
18079         return this.dragData;
18080     },
18081
18082     // private
18083     onDragEnter : function(e, id){
18084         var target = Roo.dd.DragDropMgr.getDDById(id);
18085         this.cachedTarget = target;
18086         if(this.beforeDragEnter(target, e, id) !== false){
18087             if(target.isNotifyTarget){
18088                 var status = target.notifyEnter(this, e, this.dragData);
18089                 this.proxy.setStatus(status);
18090             }else{
18091                 this.proxy.setStatus(this.dropAllowed);
18092             }
18093             
18094             if(this.afterDragEnter){
18095                 /**
18096                  * An empty function by default, but provided so that you can perform a custom action
18097                  * when the dragged item enters the drop target by providing an implementation.
18098                  * @param {Roo.dd.DragDrop} target The drop target
18099                  * @param {Event} e The event object
18100                  * @param {String} id The id of the dragged element
18101                  * @method afterDragEnter
18102                  */
18103                 this.afterDragEnter(target, e, id);
18104             }
18105         }
18106     },
18107
18108     /**
18109      * An empty function by default, but provided so that you can perform a custom action
18110      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18111      * @param {Roo.dd.DragDrop} target The drop target
18112      * @param {Event} e The event object
18113      * @param {String} id The id of the dragged element
18114      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18115      */
18116     beforeDragEnter : function(target, e, id){
18117         return true;
18118     },
18119
18120     // private
18121     alignElWithMouse: function() {
18122         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18123         this.proxy.sync();
18124     },
18125
18126     // private
18127     onDragOver : function(e, id){
18128         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18129         if(this.beforeDragOver(target, e, id) !== false){
18130             if(target.isNotifyTarget){
18131                 var status = target.notifyOver(this, e, this.dragData);
18132                 this.proxy.setStatus(status);
18133             }
18134
18135             if(this.afterDragOver){
18136                 /**
18137                  * An empty function by default, but provided so that you can perform a custom action
18138                  * while the dragged item is over the drop target by providing an implementation.
18139                  * @param {Roo.dd.DragDrop} target The drop target
18140                  * @param {Event} e The event object
18141                  * @param {String} id The id of the dragged element
18142                  * @method afterDragOver
18143                  */
18144                 this.afterDragOver(target, e, id);
18145             }
18146         }
18147     },
18148
18149     /**
18150      * An empty function by default, but provided so that you can perform a custom action
18151      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18152      * @param {Roo.dd.DragDrop} target The drop target
18153      * @param {Event} e The event object
18154      * @param {String} id The id of the dragged element
18155      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18156      */
18157     beforeDragOver : function(target, e, id){
18158         return true;
18159     },
18160
18161     // private
18162     onDragOut : function(e, id){
18163         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18164         if(this.beforeDragOut(target, e, id) !== false){
18165             if(target.isNotifyTarget){
18166                 target.notifyOut(this, e, this.dragData);
18167             }
18168             this.proxy.reset();
18169             if(this.afterDragOut){
18170                 /**
18171                  * An empty function by default, but provided so that you can perform a custom action
18172                  * after the dragged item is dragged out of the target without dropping.
18173                  * @param {Roo.dd.DragDrop} target The drop target
18174                  * @param {Event} e The event object
18175                  * @param {String} id The id of the dragged element
18176                  * @method afterDragOut
18177                  */
18178                 this.afterDragOut(target, e, id);
18179             }
18180         }
18181         this.cachedTarget = null;
18182     },
18183
18184     /**
18185      * An empty function by default, but provided so that you can perform a custom action before the dragged
18186      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18187      * @param {Roo.dd.DragDrop} target The drop target
18188      * @param {Event} e The event object
18189      * @param {String} id The id of the dragged element
18190      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18191      */
18192     beforeDragOut : function(target, e, id){
18193         return true;
18194     },
18195     
18196     // private
18197     onDragDrop : function(e, id){
18198         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18199         if(this.beforeDragDrop(target, e, id) !== false){
18200             if(target.isNotifyTarget){
18201                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18202                     this.onValidDrop(target, e, id);
18203                 }else{
18204                     this.onInvalidDrop(target, e, id);
18205                 }
18206             }else{
18207                 this.onValidDrop(target, e, id);
18208             }
18209             
18210             if(this.afterDragDrop){
18211                 /**
18212                  * An empty function by default, but provided so that you can perform a custom action
18213                  * after a valid drag drop has occurred by providing an implementation.
18214                  * @param {Roo.dd.DragDrop} target The drop target
18215                  * @param {Event} e The event object
18216                  * @param {String} id The id of the dropped element
18217                  * @method afterDragDrop
18218                  */
18219                 this.afterDragDrop(target, e, id);
18220             }
18221         }
18222         delete this.cachedTarget;
18223     },
18224
18225     /**
18226      * An empty function by default, but provided so that you can perform a custom action before the dragged
18227      * item is dropped onto the target and optionally cancel the onDragDrop.
18228      * @param {Roo.dd.DragDrop} target The drop target
18229      * @param {Event} e The event object
18230      * @param {String} id The id of the dragged element
18231      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18232      */
18233     beforeDragDrop : function(target, e, id){
18234         return true;
18235     },
18236
18237     // private
18238     onValidDrop : function(target, e, id){
18239         this.hideProxy();
18240         if(this.afterValidDrop){
18241             /**
18242              * An empty function by default, but provided so that you can perform a custom action
18243              * after a valid drop has occurred by providing an implementation.
18244              * @param {Object} target The target DD 
18245              * @param {Event} e The event object
18246              * @param {String} id The id of the dropped element
18247              * @method afterInvalidDrop
18248              */
18249             this.afterValidDrop(target, e, id);
18250         }
18251     },
18252
18253     // private
18254     getRepairXY : function(e, data){
18255         return this.el.getXY();  
18256     },
18257
18258     // private
18259     onInvalidDrop : function(target, e, id){
18260         this.beforeInvalidDrop(target, e, id);
18261         if(this.cachedTarget){
18262             if(this.cachedTarget.isNotifyTarget){
18263                 this.cachedTarget.notifyOut(this, e, this.dragData);
18264             }
18265             this.cacheTarget = null;
18266         }
18267         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18268
18269         if(this.afterInvalidDrop){
18270             /**
18271              * An empty function by default, but provided so that you can perform a custom action
18272              * after an invalid drop has occurred by providing an implementation.
18273              * @param {Event} e The event object
18274              * @param {String} id The id of the dropped element
18275              * @method afterInvalidDrop
18276              */
18277             this.afterInvalidDrop(e, id);
18278         }
18279     },
18280
18281     // private
18282     afterRepair : function(){
18283         if(Roo.enableFx){
18284             this.el.highlight(this.hlColor || "c3daf9");
18285         }
18286         this.dragging = false;
18287     },
18288
18289     /**
18290      * An empty function by default, but provided so that you can perform a custom action after an invalid
18291      * drop has occurred.
18292      * @param {Roo.dd.DragDrop} target The drop target
18293      * @param {Event} e The event object
18294      * @param {String} id The id of the dragged element
18295      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18296      */
18297     beforeInvalidDrop : function(target, e, id){
18298         return true;
18299     },
18300
18301     // private
18302     handleMouseDown : function(e){
18303         if(this.dragging) {
18304             return;
18305         }
18306         var data = this.getDragData(e);
18307         if(data && this.onBeforeDrag(data, e) !== false){
18308             this.dragData = data;
18309             this.proxy.stop();
18310             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18311         } 
18312     },
18313
18314     /**
18315      * An empty function by default, but provided so that you can perform a custom action before the initial
18316      * drag event begins and optionally cancel it.
18317      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18318      * @param {Event} e The event object
18319      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18320      */
18321     onBeforeDrag : function(data, e){
18322         return true;
18323     },
18324
18325     /**
18326      * An empty function by default, but provided so that you can perform a custom action once the initial
18327      * drag event has begun.  The drag cannot be canceled from this function.
18328      * @param {Number} x The x position of the click on the dragged object
18329      * @param {Number} y The y position of the click on the dragged object
18330      */
18331     onStartDrag : Roo.emptyFn,
18332
18333     // private - YUI override
18334     startDrag : function(x, y){
18335         this.proxy.reset();
18336         this.dragging = true;
18337         this.proxy.update("");
18338         this.onInitDrag(x, y);
18339         this.proxy.show();
18340     },
18341
18342     // private
18343     onInitDrag : function(x, y){
18344         var clone = this.el.dom.cloneNode(true);
18345         clone.id = Roo.id(); // prevent duplicate ids
18346         this.proxy.update(clone);
18347         this.onStartDrag(x, y);
18348         return true;
18349     },
18350
18351     /**
18352      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18353      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18354      */
18355     getProxy : function(){
18356         return this.proxy;  
18357     },
18358
18359     /**
18360      * Hides the drag source's {@link Roo.dd.StatusProxy}
18361      */
18362     hideProxy : function(){
18363         this.proxy.hide();  
18364         this.proxy.reset(true);
18365         this.dragging = false;
18366     },
18367
18368     // private
18369     triggerCacheRefresh : function(){
18370         Roo.dd.DDM.refreshCache(this.groups);
18371     },
18372
18373     // private - override to prevent hiding
18374     b4EndDrag: function(e) {
18375     },
18376
18377     // private - override to prevent moving
18378     endDrag : function(e){
18379         this.onEndDrag(this.dragData, e);
18380     },
18381
18382     // private
18383     onEndDrag : function(data, e){
18384     },
18385     
18386     // private - pin to cursor
18387     autoOffset : function(x, y) {
18388         this.setDelta(-12, -20);
18389     }    
18390 });/*
18391  * Based on:
18392  * Ext JS Library 1.1.1
18393  * Copyright(c) 2006-2007, Ext JS, LLC.
18394  *
18395  * Originally Released Under LGPL - original licence link has changed is not relivant.
18396  *
18397  * Fork - LGPL
18398  * <script type="text/javascript">
18399  */
18400
18401
18402 /**
18403  * @class Roo.dd.DropTarget
18404  * @extends Roo.dd.DDTarget
18405  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18406  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18407  * @constructor
18408  * @param {String/HTMLElement/Element} el The container element
18409  * @param {Object} config
18410  */
18411 Roo.dd.DropTarget = function(el, config){
18412     this.el = Roo.get(el);
18413     
18414     var listeners = false; ;
18415     if (config && config.listeners) {
18416         listeners= config.listeners;
18417         delete config.listeners;
18418     }
18419     Roo.apply(this, config);
18420     
18421     if(this.containerScroll){
18422         Roo.dd.ScrollManager.register(this.el);
18423     }
18424     this.addEvents( {
18425          /**
18426          * @scope Roo.dd.DropTarget
18427          */
18428          
18429          /**
18430          * @event enter
18431          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18432          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18433          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18434          * 
18435          * IMPORTANT : it should set this.overClass and this.dropAllowed
18436          * 
18437          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18438          * @param {Event} e The event
18439          * @param {Object} data An object containing arbitrary data supplied by the drag source
18440          */
18441         "enter" : true,
18442         
18443          /**
18444          * @event over
18445          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18446          * This method will be called on every mouse movement while the drag source is over the drop target.
18447          * This default implementation simply returns the dropAllowed config value.
18448          * 
18449          * IMPORTANT : it should set this.dropAllowed
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454          
18455          */
18456         "over" : true,
18457         /**
18458          * @event out
18459          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18460          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18461          * overClass (if any) from the drop element.
18462          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18463          * @param {Event} e The event
18464          * @param {Object} data An object containing arbitrary data supplied by the drag source
18465          */
18466          "out" : true,
18467          
18468         /**
18469          * @event drop
18470          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18471          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18472          * implementation that does something to process the drop event and returns true so that the drag source's
18473          * repair action does not run.
18474          * 
18475          * IMPORTANT : it should set this.success
18476          * 
18477          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18478          * @param {Event} e The event
18479          * @param {Object} data An object containing arbitrary data supplied by the drag source
18480         */
18481          "drop" : true
18482     });
18483             
18484      
18485     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18486         this.el.dom, 
18487         this.ddGroup || this.group,
18488         {
18489             isTarget: true,
18490             listeners : listeners || {} 
18491            
18492         
18493         }
18494     );
18495
18496 };
18497
18498 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18499     /**
18500      * @cfg {String} overClass
18501      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18502      */
18503      /**
18504      * @cfg {String} ddGroup
18505      * The drag drop group to handle drop events for
18506      */
18507      
18508     /**
18509      * @cfg {String} dropAllowed
18510      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18511      */
18512     dropAllowed : "x-dd-drop-ok",
18513     /**
18514      * @cfg {String} dropNotAllowed
18515      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18516      */
18517     dropNotAllowed : "x-dd-drop-nodrop",
18518     /**
18519      * @cfg {boolean} success
18520      * set this after drop listener.. 
18521      */
18522     success : false,
18523     /**
18524      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18525      * if the drop point is valid for over/enter..
18526      */
18527     valid : false,
18528     // private
18529     isTarget : true,
18530
18531     // private
18532     isNotifyTarget : true,
18533     
18534     /**
18535      * @hide
18536      */
18537     notifyEnter : function(dd, e, data)
18538     {
18539         this.valid = true;
18540         this.fireEvent('enter', dd, e, data);
18541         if(this.overClass){
18542             this.el.addClass(this.overClass);
18543         }
18544         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18545             this.valid ? this.dropAllowed : this.dropNotAllowed
18546         );
18547     },
18548
18549     /**
18550      * @hide
18551      */
18552     notifyOver : function(dd, e, data)
18553     {
18554         this.valid = true;
18555         this.fireEvent('over', dd, e, data);
18556         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18557             this.valid ? this.dropAllowed : this.dropNotAllowed
18558         );
18559     },
18560
18561     /**
18562      * @hide
18563      */
18564     notifyOut : function(dd, e, data)
18565     {
18566         this.fireEvent('out', dd, e, data);
18567         if(this.overClass){
18568             this.el.removeClass(this.overClass);
18569         }
18570     },
18571
18572     /**
18573      * @hide
18574      */
18575     notifyDrop : function(dd, e, data)
18576     {
18577         this.success = false;
18578         this.fireEvent('drop', dd, e, data);
18579         return this.success;
18580     }
18581 });/*
18582  * Based on:
18583  * Ext JS Library 1.1.1
18584  * Copyright(c) 2006-2007, Ext JS, LLC.
18585  *
18586  * Originally Released Under LGPL - original licence link has changed is not relivant.
18587  *
18588  * Fork - LGPL
18589  * <script type="text/javascript">
18590  */
18591
18592
18593 /**
18594  * @class Roo.dd.DragZone
18595  * @extends Roo.dd.DragSource
18596  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18597  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18598  * @constructor
18599  * @param {String/HTMLElement/Element} el The container element
18600  * @param {Object} config
18601  */
18602 Roo.dd.DragZone = function(el, config){
18603     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18604     if(this.containerScroll){
18605         Roo.dd.ScrollManager.register(this.el);
18606     }
18607 };
18608
18609 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18610     /**
18611      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18612      * for auto scrolling during drag operations.
18613      */
18614     /**
18615      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18616      * method after a failed drop (defaults to "c3daf9" - light blue)
18617      */
18618
18619     /**
18620      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18621      * for a valid target to drag based on the mouse down. Override this method
18622      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18623      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18624      * @param {EventObject} e The mouse down event
18625      * @return {Object} The dragData
18626      */
18627     getDragData : function(e){
18628         return Roo.dd.Registry.getHandleFromEvent(e);
18629     },
18630     
18631     /**
18632      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18633      * this.dragData.ddel
18634      * @param {Number} x The x position of the click on the dragged object
18635      * @param {Number} y The y position of the click on the dragged object
18636      * @return {Boolean} true to continue the drag, false to cancel
18637      */
18638     onInitDrag : function(x, y){
18639         this.proxy.update(this.dragData.ddel.cloneNode(true));
18640         this.onStartDrag(x, y);
18641         return true;
18642     },
18643     
18644     /**
18645      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18646      */
18647     afterRepair : function(){
18648         if(Roo.enableFx){
18649             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18650         }
18651         this.dragging = false;
18652     },
18653
18654     /**
18655      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18656      * the XY of this.dragData.ddel
18657      * @param {EventObject} e The mouse up event
18658      * @return {Array} The xy location (e.g. [100, 200])
18659      */
18660     getRepairXY : function(e){
18661         return Roo.Element.fly(this.dragData.ddel).getXY();  
18662     }
18663 });/*
18664  * Based on:
18665  * Ext JS Library 1.1.1
18666  * Copyright(c) 2006-2007, Ext JS, LLC.
18667  *
18668  * Originally Released Under LGPL - original licence link has changed is not relivant.
18669  *
18670  * Fork - LGPL
18671  * <script type="text/javascript">
18672  */
18673 /**
18674  * @class Roo.dd.DropZone
18675  * @extends Roo.dd.DropTarget
18676  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18677  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18678  * @constructor
18679  * @param {String/HTMLElement/Element} el The container element
18680  * @param {Object} config
18681  */
18682 Roo.dd.DropZone = function(el, config){
18683     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18684 };
18685
18686 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18687     /**
18688      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18689      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18690      * provide your own custom lookup.
18691      * @param {Event} e The event
18692      * @return {Object} data The custom data
18693      */
18694     getTargetFromEvent : function(e){
18695         return Roo.dd.Registry.getTargetFromEvent(e);
18696     },
18697
18698     /**
18699      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18700      * that it has registered.  This method has no default implementation and should be overridden to provide
18701      * node-specific processing if necessary.
18702      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18703      * {@link #getTargetFromEvent} for this node)
18704      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18705      * @param {Event} e The event
18706      * @param {Object} data An object containing arbitrary data supplied by the drag source
18707      */
18708     onNodeEnter : function(n, dd, e, data){
18709         
18710     },
18711
18712     /**
18713      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18714      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18715      * overridden to provide the proper feedback.
18716      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18717      * {@link #getTargetFromEvent} for this node)
18718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18719      * @param {Event} e The event
18720      * @param {Object} data An object containing arbitrary data supplied by the drag source
18721      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18722      * underlying {@link Roo.dd.StatusProxy} can be updated
18723      */
18724     onNodeOver : function(n, dd, e, data){
18725         return this.dropAllowed;
18726     },
18727
18728     /**
18729      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18730      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18731      * node-specific processing if necessary.
18732      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18733      * {@link #getTargetFromEvent} for this node)
18734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18735      * @param {Event} e The event
18736      * @param {Object} data An object containing arbitrary data supplied by the drag source
18737      */
18738     onNodeOut : function(n, dd, e, data){
18739         
18740     },
18741
18742     /**
18743      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18744      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18745      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18746      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18747      * {@link #getTargetFromEvent} for this node)
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {Boolean} True if the drop was valid, else false
18752      */
18753     onNodeDrop : function(n, dd, e, data){
18754         return false;
18755     },
18756
18757     /**
18758      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18759      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18760      * it should be overridden to provide the proper feedback if necessary.
18761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18762      * @param {Event} e The event
18763      * @param {Object} data An object containing arbitrary data supplied by the drag source
18764      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18765      * underlying {@link Roo.dd.StatusProxy} can be updated
18766      */
18767     onContainerOver : function(dd, e, data){
18768         return this.dropNotAllowed;
18769     },
18770
18771     /**
18772      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18773      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18774      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18775      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18776      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18777      * @param {Event} e The event
18778      * @param {Object} data An object containing arbitrary data supplied by the drag source
18779      * @return {Boolean} True if the drop was valid, else false
18780      */
18781     onContainerDrop : function(dd, e, data){
18782         return false;
18783     },
18784
18785     /**
18786      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18787      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18788      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18789      * you should override this method and provide a custom implementation.
18790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18791      * @param {Event} e The event
18792      * @param {Object} data An object containing arbitrary data supplied by the drag source
18793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18794      * underlying {@link Roo.dd.StatusProxy} can be updated
18795      */
18796     notifyEnter : function(dd, e, data){
18797         return this.dropNotAllowed;
18798     },
18799
18800     /**
18801      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18802      * This method will be called on every mouse movement while the drag source is over the drop zone.
18803      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18804      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18805      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18806      * registered node, it will call {@link #onContainerOver}.
18807      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18808      * @param {Event} e The event
18809      * @param {Object} data An object containing arbitrary data supplied by the drag source
18810      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18811      * underlying {@link Roo.dd.StatusProxy} can be updated
18812      */
18813     notifyOver : function(dd, e, data){
18814         var n = this.getTargetFromEvent(e);
18815         if(!n){ // not over valid drop target
18816             if(this.lastOverNode){
18817                 this.onNodeOut(this.lastOverNode, dd, e, data);
18818                 this.lastOverNode = null;
18819             }
18820             return this.onContainerOver(dd, e, data);
18821         }
18822         if(this.lastOverNode != n){
18823             if(this.lastOverNode){
18824                 this.onNodeOut(this.lastOverNode, dd, e, data);
18825             }
18826             this.onNodeEnter(n, dd, e, data);
18827             this.lastOverNode = n;
18828         }
18829         return this.onNodeOver(n, dd, e, data);
18830     },
18831
18832     /**
18833      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18834      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18835      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18836      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18837      * @param {Event} e The event
18838      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18839      */
18840     notifyOut : function(dd, e, data){
18841         if(this.lastOverNode){
18842             this.onNodeOut(this.lastOverNode, dd, e, data);
18843             this.lastOverNode = null;
18844         }
18845     },
18846
18847     /**
18848      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18849      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18850      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18851      * otherwise it will call {@link #onContainerDrop}.
18852      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18853      * @param {Event} e The event
18854      * @param {Object} data An object containing arbitrary data supplied by the drag source
18855      * @return {Boolean} True if the drop was valid, else false
18856      */
18857     notifyDrop : function(dd, e, data){
18858         if(this.lastOverNode){
18859             this.onNodeOut(this.lastOverNode, dd, e, data);
18860             this.lastOverNode = null;
18861         }
18862         var n = this.getTargetFromEvent(e);
18863         return n ?
18864             this.onNodeDrop(n, dd, e, data) :
18865             this.onContainerDrop(dd, e, data);
18866     },
18867
18868     // private
18869     triggerCacheRefresh : function(){
18870         Roo.dd.DDM.refreshCache(this.groups);
18871     }  
18872 });/*
18873  * Based on:
18874  * Ext JS Library 1.1.1
18875  * Copyright(c) 2006-2007, Ext JS, LLC.
18876  *
18877  * Originally Released Under LGPL - original licence link has changed is not relivant.
18878  *
18879  * Fork - LGPL
18880  * <script type="text/javascript">
18881  */
18882
18883
18884 /**
18885  * @class Roo.data.SortTypes
18886  * @singleton
18887  * Defines the default sorting (casting?) comparison functions used when sorting data.
18888  */
18889 Roo.data.SortTypes = {
18890     /**
18891      * Default sort that does nothing
18892      * @param {Mixed} s The value being converted
18893      * @return {Mixed} The comparison value
18894      */
18895     none : function(s){
18896         return s;
18897     },
18898     
18899     /**
18900      * The regular expression used to strip tags
18901      * @type {RegExp}
18902      * @property
18903      */
18904     stripTagsRE : /<\/?[^>]+>/gi,
18905     
18906     /**
18907      * Strips all HTML tags to sort on text only
18908      * @param {Mixed} s The value being converted
18909      * @return {String} The comparison value
18910      */
18911     asText : function(s){
18912         return String(s).replace(this.stripTagsRE, "");
18913     },
18914     
18915     /**
18916      * Strips all HTML tags to sort on text only - Case insensitive
18917      * @param {Mixed} s The value being converted
18918      * @return {String} The comparison value
18919      */
18920     asUCText : function(s){
18921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18922     },
18923     
18924     /**
18925      * Case insensitive string
18926      * @param {Mixed} s The value being converted
18927      * @return {String} The comparison value
18928      */
18929     asUCString : function(s) {
18930         return String(s).toUpperCase();
18931     },
18932     
18933     /**
18934      * Date sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asDate : function(s) {
18939         if(!s){
18940             return 0;
18941         }
18942         if(s instanceof Date){
18943             return s.getTime();
18944         }
18945         return Date.parse(String(s));
18946     },
18947     
18948     /**
18949      * Float sorting
18950      * @param {Mixed} s The value being converted
18951      * @return {Float} The comparison value
18952      */
18953     asFloat : function(s) {
18954         var val = parseFloat(String(s).replace(/,/g, ""));
18955         if(isNaN(val)) val = 0;
18956         return val;
18957     },
18958     
18959     /**
18960      * Integer sorting
18961      * @param {Mixed} s The value being converted
18962      * @return {Number} The comparison value
18963      */
18964     asInt : function(s) {
18965         var val = parseInt(String(s).replace(/,/g, ""));
18966         if(isNaN(val)) val = 0;
18967         return val;
18968     }
18969 };/*
18970  * Based on:
18971  * Ext JS Library 1.1.1
18972  * Copyright(c) 2006-2007, Ext JS, LLC.
18973  *
18974  * Originally Released Under LGPL - original licence link has changed is not relivant.
18975  *
18976  * Fork - LGPL
18977  * <script type="text/javascript">
18978  */
18979
18980 /**
18981 * @class Roo.data.Record
18982  * Instances of this class encapsulate both record <em>definition</em> information, and record
18983  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18984  * to access Records cached in an {@link Roo.data.Store} object.<br>
18985  * <p>
18986  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18987  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18988  * objects.<br>
18989  * <p>
18990  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18991  * @constructor
18992  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18993  * {@link #create}. The parameters are the same.
18994  * @param {Array} data An associative Array of data values keyed by the field name.
18995  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18996  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18997  * not specified an integer id is generated.
18998  */
18999 Roo.data.Record = function(data, id){
19000     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19001     this.data = data;
19002 };
19003
19004 /**
19005  * Generate a constructor for a specific record layout.
19006  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19007  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19008  * Each field definition object may contain the following properties: <ul>
19009  * <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,
19010  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19011  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19012  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19013  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19014  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19015  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19016  * this may be omitted.</p></li>
19017  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19018  * <ul><li>auto (Default, implies no conversion)</li>
19019  * <li>string</li>
19020  * <li>int</li>
19021  * <li>float</li>
19022  * <li>boolean</li>
19023  * <li>date</li></ul></p></li>
19024  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19025  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19026  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19027  * by the Reader into an object that will be stored in the Record. It is passed the
19028  * following parameters:<ul>
19029  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19030  * </ul></p></li>
19031  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19032  * </ul>
19033  * <br>usage:<br><pre><code>
19034 var TopicRecord = Roo.data.Record.create(
19035     {name: 'title', mapping: 'topic_title'},
19036     {name: 'author', mapping: 'username'},
19037     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19038     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19039     {name: 'lastPoster', mapping: 'user2'},
19040     {name: 'excerpt', mapping: 'post_text'}
19041 );
19042
19043 var myNewRecord = new TopicRecord({
19044     title: 'Do my job please',
19045     author: 'noobie',
19046     totalPosts: 1,
19047     lastPost: new Date(),
19048     lastPoster: 'Animal',
19049     excerpt: 'No way dude!'
19050 });
19051 myStore.add(myNewRecord);
19052 </code></pre>
19053  * @method create
19054  * @static
19055  */
19056 Roo.data.Record.create = function(o){
19057     var f = function(){
19058         f.superclass.constructor.apply(this, arguments);
19059     };
19060     Roo.extend(f, Roo.data.Record);
19061     var p = f.prototype;
19062     p.fields = new Roo.util.MixedCollection(false, function(field){
19063         return field.name;
19064     });
19065     for(var i = 0, len = o.length; i < len; i++){
19066         p.fields.add(new Roo.data.Field(o[i]));
19067     }
19068     f.getField = function(name){
19069         return p.fields.get(name);  
19070     };
19071     return f;
19072 };
19073
19074 Roo.data.Record.AUTO_ID = 1000;
19075 Roo.data.Record.EDIT = 'edit';
19076 Roo.data.Record.REJECT = 'reject';
19077 Roo.data.Record.COMMIT = 'commit';
19078
19079 Roo.data.Record.prototype = {
19080     /**
19081      * Readonly flag - true if this record has been modified.
19082      * @type Boolean
19083      */
19084     dirty : false,
19085     editing : false,
19086     error: null,
19087     modified: null,
19088
19089     // private
19090     join : function(store){
19091         this.store = store;
19092     },
19093
19094     /**
19095      * Set the named field to the specified value.
19096      * @param {String} name The name of the field to set.
19097      * @param {Object} value The value to set the field to.
19098      */
19099     set : function(name, value){
19100         if(this.data[name] == value){
19101             return;
19102         }
19103         this.dirty = true;
19104         if(!this.modified){
19105             this.modified = {};
19106         }
19107         if(typeof this.modified[name] == 'undefined'){
19108             this.modified[name] = this.data[name];
19109         }
19110         this.data[name] = value;
19111         if(!this.editing && this.store){
19112             this.store.afterEdit(this);
19113         }       
19114     },
19115
19116     /**
19117      * Get the value of the named field.
19118      * @param {String} name The name of the field to get the value of.
19119      * @return {Object} The value of the field.
19120      */
19121     get : function(name){
19122         return this.data[name]; 
19123     },
19124
19125     // private
19126     beginEdit : function(){
19127         this.editing = true;
19128         this.modified = {}; 
19129     },
19130
19131     // private
19132     cancelEdit : function(){
19133         this.editing = false;
19134         delete this.modified;
19135     },
19136
19137     // private
19138     endEdit : function(){
19139         this.editing = false;
19140         if(this.dirty && this.store){
19141             this.store.afterEdit(this);
19142         }
19143     },
19144
19145     /**
19146      * Usually called by the {@link Roo.data.Store} which owns the Record.
19147      * Rejects all changes made to the Record since either creation, or the last commit operation.
19148      * Modified fields are reverted to their original values.
19149      * <p>
19150      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19151      * of reject operations.
19152      */
19153     reject : function(){
19154         var m = this.modified;
19155         for(var n in m){
19156             if(typeof m[n] != "function"){
19157                 this.data[n] = m[n];
19158             }
19159         }
19160         this.dirty = false;
19161         delete this.modified;
19162         this.editing = false;
19163         if(this.store){
19164             this.store.afterReject(this);
19165         }
19166     },
19167
19168     /**
19169      * Usually called by the {@link Roo.data.Store} which owns the Record.
19170      * Commits all changes made to the Record since either creation, or the last commit operation.
19171      * <p>
19172      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19173      * of commit operations.
19174      */
19175     commit : function(){
19176         this.dirty = false;
19177         delete this.modified;
19178         this.editing = false;
19179         if(this.store){
19180             this.store.afterCommit(this);
19181         }
19182     },
19183
19184     // private
19185     hasError : function(){
19186         return this.error != null;
19187     },
19188
19189     // private
19190     clearError : function(){
19191         this.error = null;
19192     },
19193
19194     /**
19195      * Creates a copy of this record.
19196      * @param {String} id (optional) A new record id if you don't want to use this record's id
19197      * @return {Record}
19198      */
19199     copy : function(newId) {
19200         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19201     }
19202 };/*
19203  * Based on:
19204  * Ext JS Library 1.1.1
19205  * Copyright(c) 2006-2007, Ext JS, LLC.
19206  *
19207  * Originally Released Under LGPL - original licence link has changed is not relivant.
19208  *
19209  * Fork - LGPL
19210  * <script type="text/javascript">
19211  */
19212
19213
19214
19215 /**
19216  * @class Roo.data.Store
19217  * @extends Roo.util.Observable
19218  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19219  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19220  * <p>
19221  * 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
19222  * has no knowledge of the format of the data returned by the Proxy.<br>
19223  * <p>
19224  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19225  * instances from the data object. These records are cached and made available through accessor functions.
19226  * @constructor
19227  * Creates a new Store.
19228  * @param {Object} config A config object containing the objects needed for the Store to access data,
19229  * and read the data into Records.
19230  */
19231 Roo.data.Store = function(config){
19232     this.data = new Roo.util.MixedCollection(false);
19233     this.data.getKey = function(o){
19234         return o.id;
19235     };
19236     this.baseParams = {};
19237     // private
19238     this.paramNames = {
19239         "start" : "start",
19240         "limit" : "limit",
19241         "sort" : "sort",
19242         "dir" : "dir",
19243         "multisort" : "_multisort"
19244     };
19245
19246     if(config && config.data){
19247         this.inlineData = config.data;
19248         delete config.data;
19249     }
19250
19251     Roo.apply(this, config);
19252     
19253     if(this.reader){ // reader passed
19254         this.reader = Roo.factory(this.reader, Roo.data);
19255         this.reader.xmodule = this.xmodule || false;
19256         if(!this.recordType){
19257             this.recordType = this.reader.recordType;
19258         }
19259         if(this.reader.onMetaChange){
19260             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19261         }
19262     }
19263
19264     if(this.recordType){
19265         this.fields = this.recordType.prototype.fields;
19266     }
19267     this.modified = [];
19268
19269     this.addEvents({
19270         /**
19271          * @event datachanged
19272          * Fires when the data cache has changed, and a widget which is using this Store
19273          * as a Record cache should refresh its view.
19274          * @param {Store} this
19275          */
19276         datachanged : true,
19277         /**
19278          * @event metachange
19279          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19280          * @param {Store} this
19281          * @param {Object} meta The JSON metadata
19282          */
19283         metachange : true,
19284         /**
19285          * @event add
19286          * Fires when Records have been added to the Store
19287          * @param {Store} this
19288          * @param {Roo.data.Record[]} records The array of Records added
19289          * @param {Number} index The index at which the record(s) were added
19290          */
19291         add : true,
19292         /**
19293          * @event remove
19294          * Fires when a Record has been removed from the Store
19295          * @param {Store} this
19296          * @param {Roo.data.Record} record The Record that was removed
19297          * @param {Number} index The index at which the record was removed
19298          */
19299         remove : true,
19300         /**
19301          * @event update
19302          * Fires when a Record has been updated
19303          * @param {Store} this
19304          * @param {Roo.data.Record} record The Record that was updated
19305          * @param {String} operation The update operation being performed.  Value may be one of:
19306          * <pre><code>
19307  Roo.data.Record.EDIT
19308  Roo.data.Record.REJECT
19309  Roo.data.Record.COMMIT
19310          * </code></pre>
19311          */
19312         update : true,
19313         /**
19314          * @event clear
19315          * Fires when the data cache has been cleared.
19316          * @param {Store} this
19317          */
19318         clear : true,
19319         /**
19320          * @event beforeload
19321          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19322          * the load action will be canceled.
19323          * @param {Store} this
19324          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19325          */
19326         beforeload : true,
19327         /**
19328          * @event load
19329          * Fires after a new set of Records has been loaded.
19330          * @param {Store} this
19331          * @param {Roo.data.Record[]} records The Records that were loaded
19332          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19333          */
19334         load : true,
19335         /**
19336          * @event loadexception
19337          * Fires if an exception occurs in the Proxy during loading.
19338          * Called with the signature of the Proxy's "loadexception" event.
19339          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19340          * 
19341          * @param {Proxy} 
19342          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19343          * @param {Object} load options 
19344          * @param {Object} jsonData from your request (normally this contains the Exception)
19345          */
19346         loadexception : true
19347     });
19348     
19349     if(this.proxy){
19350         this.proxy = Roo.factory(this.proxy, Roo.data);
19351         this.proxy.xmodule = this.xmodule || false;
19352         this.relayEvents(this.proxy,  ["loadexception"]);
19353     }
19354     this.sortToggle = {};
19355     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19356
19357     Roo.data.Store.superclass.constructor.call(this);
19358
19359     if(this.inlineData){
19360         this.loadData(this.inlineData);
19361         delete this.inlineData;
19362     }
19363 };
19364 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19365      /**
19366     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19367     * without a remote query - used by combo/forms at present.
19368     */
19369     
19370     /**
19371     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19372     */
19373     /**
19374     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19375     */
19376     /**
19377     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19378     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19379     */
19380     /**
19381     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19382     * on any HTTP request
19383     */
19384     /**
19385     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19386     */
19387     /**
19388     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19389     */
19390     multiSort: false,
19391     /**
19392     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19393     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19394     */
19395     remoteSort : false,
19396
19397     /**
19398     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19399      * loaded or when a record is removed. (defaults to false).
19400     */
19401     pruneModifiedRecords : false,
19402
19403     // private
19404     lastOptions : null,
19405
19406     /**
19407      * Add Records to the Store and fires the add event.
19408      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19409      */
19410     add : function(records){
19411         records = [].concat(records);
19412         for(var i = 0, len = records.length; i < len; i++){
19413             records[i].join(this);
19414         }
19415         var index = this.data.length;
19416         this.data.addAll(records);
19417         this.fireEvent("add", this, records, index);
19418     },
19419
19420     /**
19421      * Remove a Record from the Store and fires the remove event.
19422      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19423      */
19424     remove : function(record){
19425         var index = this.data.indexOf(record);
19426         this.data.removeAt(index);
19427         if(this.pruneModifiedRecords){
19428             this.modified.remove(record);
19429         }
19430         this.fireEvent("remove", this, record, index);
19431     },
19432
19433     /**
19434      * Remove all Records from the Store and fires the clear event.
19435      */
19436     removeAll : function(){
19437         this.data.clear();
19438         if(this.pruneModifiedRecords){
19439             this.modified = [];
19440         }
19441         this.fireEvent("clear", this);
19442     },
19443
19444     /**
19445      * Inserts Records to the Store at the given index and fires the add event.
19446      * @param {Number} index The start index at which to insert the passed Records.
19447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19448      */
19449     insert : function(index, records){
19450         records = [].concat(records);
19451         for(var i = 0, len = records.length; i < len; i++){
19452             this.data.insert(index, records[i]);
19453             records[i].join(this);
19454         }
19455         this.fireEvent("add", this, records, index);
19456     },
19457
19458     /**
19459      * Get the index within the cache of the passed Record.
19460      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19461      * @return {Number} The index of the passed Record. Returns -1 if not found.
19462      */
19463     indexOf : function(record){
19464         return this.data.indexOf(record);
19465     },
19466
19467     /**
19468      * Get the index within the cache of the Record with the passed id.
19469      * @param {String} id The id of the Record to find.
19470      * @return {Number} The index of the Record. Returns -1 if not found.
19471      */
19472     indexOfId : function(id){
19473         return this.data.indexOfKey(id);
19474     },
19475
19476     /**
19477      * Get the Record with the specified id.
19478      * @param {String} id The id of the Record to find.
19479      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19480      */
19481     getById : function(id){
19482         return this.data.key(id);
19483     },
19484
19485     /**
19486      * Get the Record at the specified index.
19487      * @param {Number} index The index of the Record to find.
19488      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19489      */
19490     getAt : function(index){
19491         return this.data.itemAt(index);
19492     },
19493
19494     /**
19495      * Returns a range of Records between specified indices.
19496      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19497      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19498      * @return {Roo.data.Record[]} An array of Records
19499      */
19500     getRange : function(start, end){
19501         return this.data.getRange(start, end);
19502     },
19503
19504     // private
19505     storeOptions : function(o){
19506         o = Roo.apply({}, o);
19507         delete o.callback;
19508         delete o.scope;
19509         this.lastOptions = o;
19510     },
19511
19512     /**
19513      * Loads the Record cache from the configured Proxy using the configured Reader.
19514      * <p>
19515      * If using remote paging, then the first load call must specify the <em>start</em>
19516      * and <em>limit</em> properties in the options.params property to establish the initial
19517      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19518      * <p>
19519      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19520      * and this call will return before the new data has been loaded. Perform any post-processing
19521      * in a callback function, or in a "load" event handler.</strong>
19522      * <p>
19523      * @param {Object} options An object containing properties which control loading options:<ul>
19524      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19525      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19526      * passed the following arguments:<ul>
19527      * <li>r : Roo.data.Record[]</li>
19528      * <li>options: Options object from the load call</li>
19529      * <li>success: Boolean success indicator</li></ul></li>
19530      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19531      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19532      * </ul>
19533      */
19534     load : function(options){
19535         options = options || {};
19536         if(this.fireEvent("beforeload", this, options) !== false){
19537             this.storeOptions(options);
19538             var p = Roo.apply(options.params || {}, this.baseParams);
19539             // if meta was not loaded from remote source.. try requesting it.
19540             if (!this.reader.metaFromRemote) {
19541                 p._requestMeta = 1;
19542             }
19543             if(this.sortInfo && this.remoteSort){
19544                 var pn = this.paramNames;
19545                 p[pn["sort"]] = this.sortInfo.field;
19546                 p[pn["dir"]] = this.sortInfo.direction;
19547             }
19548             if (this.multiSort) {
19549                 var pn = this.paramNames;
19550                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19551             }
19552             
19553             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19554         }
19555     },
19556
19557     /**
19558      * Reloads the Record cache from the configured Proxy using the configured Reader and
19559      * the options from the last load operation performed.
19560      * @param {Object} options (optional) An object containing properties which may override the options
19561      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19562      * the most recently used options are reused).
19563      */
19564     reload : function(options){
19565         this.load(Roo.applyIf(options||{}, this.lastOptions));
19566     },
19567
19568     // private
19569     // Called as a callback by the Reader during a load operation.
19570     loadRecords : function(o, options, success){
19571         if(!o || success === false){
19572             if(success !== false){
19573                 this.fireEvent("load", this, [], options);
19574             }
19575             if(options.callback){
19576                 options.callback.call(options.scope || this, [], options, false);
19577             }
19578             return;
19579         }
19580         // if data returned failure - throw an exception.
19581         if (o.success === false) {
19582             // show a message if no listener is registered.
19583             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19584                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19585             }
19586             // loadmask wil be hooked into this..
19587             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19588             return;
19589         }
19590         var r = o.records, t = o.totalRecords || r.length;
19591         if(!options || options.add !== true){
19592             if(this.pruneModifiedRecords){
19593                 this.modified = [];
19594             }
19595             for(var i = 0, len = r.length; i < len; i++){
19596                 r[i].join(this);
19597             }
19598             if(this.snapshot){
19599                 this.data = this.snapshot;
19600                 delete this.snapshot;
19601             }
19602             this.data.clear();
19603             this.data.addAll(r);
19604             this.totalLength = t;
19605             this.applySort();
19606             this.fireEvent("datachanged", this);
19607         }else{
19608             this.totalLength = Math.max(t, this.data.length+r.length);
19609             this.add(r);
19610         }
19611         this.fireEvent("load", this, r, options);
19612         if(options.callback){
19613             options.callback.call(options.scope || this, r, options, true);
19614         }
19615     },
19616
19617
19618     /**
19619      * Loads data from a passed data block. A Reader which understands the format of the data
19620      * must have been configured in the constructor.
19621      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19622      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19623      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19624      */
19625     loadData : function(o, append){
19626         var r = this.reader.readRecords(o);
19627         this.loadRecords(r, {add: append}, true);
19628     },
19629
19630     /**
19631      * Gets the number of cached records.
19632      * <p>
19633      * <em>If using paging, this may not be the total size of the dataset. If the data object
19634      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19635      * the data set size</em>
19636      */
19637     getCount : function(){
19638         return this.data.length || 0;
19639     },
19640
19641     /**
19642      * Gets the total number of records in the dataset as returned by the server.
19643      * <p>
19644      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19645      * the dataset size</em>
19646      */
19647     getTotalCount : function(){
19648         return this.totalLength || 0;
19649     },
19650
19651     /**
19652      * Returns the sort state of the Store as an object with two properties:
19653      * <pre><code>
19654  field {String} The name of the field by which the Records are sorted
19655  direction {String} The sort order, "ASC" or "DESC"
19656      * </code></pre>
19657      */
19658     getSortState : function(){
19659         return this.sortInfo;
19660     },
19661
19662     // private
19663     applySort : function(){
19664         if(this.sortInfo && !this.remoteSort){
19665             var s = this.sortInfo, f = s.field;
19666             var st = this.fields.get(f).sortType;
19667             var fn = function(r1, r2){
19668                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19669                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19670             };
19671             this.data.sort(s.direction, fn);
19672             if(this.snapshot && this.snapshot != this.data){
19673                 this.snapshot.sort(s.direction, fn);
19674             }
19675         }
19676     },
19677
19678     /**
19679      * Sets the default sort column and order to be used by the next load operation.
19680      * @param {String} fieldName The name of the field to sort by.
19681      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19682      */
19683     setDefaultSort : function(field, dir){
19684         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19685     },
19686
19687     /**
19688      * Sort the Records.
19689      * If remote sorting is used, the sort is performed on the server, and the cache is
19690      * reloaded. If local sorting is used, the cache is sorted internally.
19691      * @param {String} fieldName The name of the field to sort by.
19692      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19693      */
19694     sort : function(fieldName, dir){
19695         var f = this.fields.get(fieldName);
19696         if(!dir){
19697             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19698             
19699             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19700                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19701             }else{
19702                 dir = f.sortDir;
19703             }
19704         }
19705         this.sortToggle[f.name] = dir;
19706         this.sortInfo = {field: f.name, direction: dir};
19707         if(!this.remoteSort){
19708             this.applySort();
19709             this.fireEvent("datachanged", this);
19710         }else{
19711             this.load(this.lastOptions);
19712         }
19713     },
19714
19715     /**
19716      * Calls the specified function for each of the Records in the cache.
19717      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19718      * Returning <em>false</em> aborts and exits the iteration.
19719      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19720      */
19721     each : function(fn, scope){
19722         this.data.each(fn, scope);
19723     },
19724
19725     /**
19726      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19727      * (e.g., during paging).
19728      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19729      */
19730     getModifiedRecords : function(){
19731         return this.modified;
19732     },
19733
19734     // private
19735     createFilterFn : function(property, value, anyMatch){
19736         if(!value.exec){ // not a regex
19737             value = String(value);
19738             if(value.length == 0){
19739                 return false;
19740             }
19741             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19742         }
19743         return function(r){
19744             return value.test(r.data[property]);
19745         };
19746     },
19747
19748     /**
19749      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19750      * @param {String} property A field on your records
19751      * @param {Number} start The record index to start at (defaults to 0)
19752      * @param {Number} end The last record index to include (defaults to length - 1)
19753      * @return {Number} The sum
19754      */
19755     sum : function(property, start, end){
19756         var rs = this.data.items, v = 0;
19757         start = start || 0;
19758         end = (end || end === 0) ? end : rs.length-1;
19759
19760         for(var i = start; i <= end; i++){
19761             v += (rs[i].data[property] || 0);
19762         }
19763         return v;
19764     },
19765
19766     /**
19767      * Filter the records by a specified property.
19768      * @param {String} field A field on your records
19769      * @param {String/RegExp} value Either a string that the field
19770      * should start with or a RegExp to test against the field
19771      * @param {Boolean} anyMatch True to match any part not just the beginning
19772      */
19773     filter : function(property, value, anyMatch){
19774         var fn = this.createFilterFn(property, value, anyMatch);
19775         return fn ? this.filterBy(fn) : this.clearFilter();
19776     },
19777
19778     /**
19779      * Filter by a function. The specified function will be called with each
19780      * record in this data source. If the function returns true the record is included,
19781      * otherwise it is filtered.
19782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19783      * @param {Object} scope (optional) The scope of the function (defaults to this)
19784      */
19785     filterBy : function(fn, scope){
19786         this.snapshot = this.snapshot || this.data;
19787         this.data = this.queryBy(fn, scope||this);
19788         this.fireEvent("datachanged", this);
19789     },
19790
19791     /**
19792      * Query the records by a specified property.
19793      * @param {String} field A field on your records
19794      * @param {String/RegExp} value Either a string that the field
19795      * should start with or a RegExp to test against the field
19796      * @param {Boolean} anyMatch True to match any part not just the beginning
19797      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19798      */
19799     query : function(property, value, anyMatch){
19800         var fn = this.createFilterFn(property, value, anyMatch);
19801         return fn ? this.queryBy(fn) : this.data.clone();
19802     },
19803
19804     /**
19805      * Query by a function. The specified function will be called with each
19806      * record in this data source. If the function returns true the record is included
19807      * in the results.
19808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19809      * @param {Object} scope (optional) The scope of the function (defaults to this)
19810       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19811      **/
19812     queryBy : function(fn, scope){
19813         var data = this.snapshot || this.data;
19814         return data.filterBy(fn, scope||this);
19815     },
19816
19817     /**
19818      * Collects unique values for a particular dataIndex from this store.
19819      * @param {String} dataIndex The property to collect
19820      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19821      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19822      * @return {Array} An array of the unique values
19823      **/
19824     collect : function(dataIndex, allowNull, bypassFilter){
19825         var d = (bypassFilter === true && this.snapshot) ?
19826                 this.snapshot.items : this.data.items;
19827         var v, sv, r = [], l = {};
19828         for(var i = 0, len = d.length; i < len; i++){
19829             v = d[i].data[dataIndex];
19830             sv = String(v);
19831             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19832                 l[sv] = true;
19833                 r[r.length] = v;
19834             }
19835         }
19836         return r;
19837     },
19838
19839     /**
19840      * Revert to a view of the Record cache with no filtering applied.
19841      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19842      */
19843     clearFilter : function(suppressEvent){
19844         if(this.snapshot && this.snapshot != this.data){
19845             this.data = this.snapshot;
19846             delete this.snapshot;
19847             if(suppressEvent !== true){
19848                 this.fireEvent("datachanged", this);
19849             }
19850         }
19851     },
19852
19853     // private
19854     afterEdit : function(record){
19855         if(this.modified.indexOf(record) == -1){
19856             this.modified.push(record);
19857         }
19858         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19859     },
19860     
19861     // private
19862     afterReject : function(record){
19863         this.modified.remove(record);
19864         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19865     },
19866
19867     // private
19868     afterCommit : function(record){
19869         this.modified.remove(record);
19870         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19871     },
19872
19873     /**
19874      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19875      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19876      */
19877     commitChanges : function(){
19878         var m = this.modified.slice(0);
19879         this.modified = [];
19880         for(var i = 0, len = m.length; i < len; i++){
19881             m[i].commit();
19882         }
19883     },
19884
19885     /**
19886      * Cancel outstanding changes on all changed records.
19887      */
19888     rejectChanges : function(){
19889         var m = this.modified.slice(0);
19890         this.modified = [];
19891         for(var i = 0, len = m.length; i < len; i++){
19892             m[i].reject();
19893         }
19894     },
19895
19896     onMetaChange : function(meta, rtype, o){
19897         this.recordType = rtype;
19898         this.fields = rtype.prototype.fields;
19899         delete this.snapshot;
19900         this.sortInfo = meta.sortInfo || this.sortInfo;
19901         this.modified = [];
19902         this.fireEvent('metachange', this, this.reader.meta);
19903     }
19904 });/*
19905  * Based on:
19906  * Ext JS Library 1.1.1
19907  * Copyright(c) 2006-2007, Ext JS, LLC.
19908  *
19909  * Originally Released Under LGPL - original licence link has changed is not relivant.
19910  *
19911  * Fork - LGPL
19912  * <script type="text/javascript">
19913  */
19914
19915 /**
19916  * @class Roo.data.SimpleStore
19917  * @extends Roo.data.Store
19918  * Small helper class to make creating Stores from Array data easier.
19919  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19920  * @cfg {Array} fields An array of field definition objects, or field name strings.
19921  * @cfg {Array} data The multi-dimensional array of data
19922  * @constructor
19923  * @param {Object} config
19924  */
19925 Roo.data.SimpleStore = function(config){
19926     Roo.data.SimpleStore.superclass.constructor.call(this, {
19927         isLocal : true,
19928         reader: new Roo.data.ArrayReader({
19929                 id: config.id
19930             },
19931             Roo.data.Record.create(config.fields)
19932         ),
19933         proxy : new Roo.data.MemoryProxy(config.data)
19934     });
19935     this.load();
19936 };
19937 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19938  * Based on:
19939  * Ext JS Library 1.1.1
19940  * Copyright(c) 2006-2007, Ext JS, LLC.
19941  *
19942  * Originally Released Under LGPL - original licence link has changed is not relivant.
19943  *
19944  * Fork - LGPL
19945  * <script type="text/javascript">
19946  */
19947
19948 /**
19949 /**
19950  * @extends Roo.data.Store
19951  * @class Roo.data.JsonStore
19952  * Small helper class to make creating Stores for JSON data easier. <br/>
19953 <pre><code>
19954 var store = new Roo.data.JsonStore({
19955     url: 'get-images.php',
19956     root: 'images',
19957     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19958 });
19959 </code></pre>
19960  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19961  * JsonReader and HttpProxy (unless inline data is provided).</b>
19962  * @cfg {Array} fields An array of field definition objects, or field name strings.
19963  * @constructor
19964  * @param {Object} config
19965  */
19966 Roo.data.JsonStore = function(c){
19967     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19968         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19969         reader: new Roo.data.JsonReader(c, c.fields)
19970     }));
19971 };
19972 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19973  * Based on:
19974  * Ext JS Library 1.1.1
19975  * Copyright(c) 2006-2007, Ext JS, LLC.
19976  *
19977  * Originally Released Under LGPL - original licence link has changed is not relivant.
19978  *
19979  * Fork - LGPL
19980  * <script type="text/javascript">
19981  */
19982
19983  
19984 Roo.data.Field = function(config){
19985     if(typeof config == "string"){
19986         config = {name: config};
19987     }
19988     Roo.apply(this, config);
19989     
19990     if(!this.type){
19991         this.type = "auto";
19992     }
19993     
19994     var st = Roo.data.SortTypes;
19995     // named sortTypes are supported, here we look them up
19996     if(typeof this.sortType == "string"){
19997         this.sortType = st[this.sortType];
19998     }
19999     
20000     // set default sortType for strings and dates
20001     if(!this.sortType){
20002         switch(this.type){
20003             case "string":
20004                 this.sortType = st.asUCString;
20005                 break;
20006             case "date":
20007                 this.sortType = st.asDate;
20008                 break;
20009             default:
20010                 this.sortType = st.none;
20011         }
20012     }
20013
20014     // define once
20015     var stripRe = /[\$,%]/g;
20016
20017     // prebuilt conversion function for this field, instead of
20018     // switching every time we're reading a value
20019     if(!this.convert){
20020         var cv, dateFormat = this.dateFormat;
20021         switch(this.type){
20022             case "":
20023             case "auto":
20024             case undefined:
20025                 cv = function(v){ return v; };
20026                 break;
20027             case "string":
20028                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20029                 break;
20030             case "int":
20031                 cv = function(v){
20032                     return v !== undefined && v !== null && v !== '' ?
20033                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20034                     };
20035                 break;
20036             case "float":
20037                 cv = function(v){
20038                     return v !== undefined && v !== null && v !== '' ?
20039                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20040                     };
20041                 break;
20042             case "bool":
20043             case "boolean":
20044                 cv = function(v){ return v === true || v === "true" || v == 1; };
20045                 break;
20046             case "date":
20047                 cv = function(v){
20048                     if(!v){
20049                         return '';
20050                     }
20051                     if(v instanceof Date){
20052                         return v;
20053                     }
20054                     if(dateFormat){
20055                         if(dateFormat == "timestamp"){
20056                             return new Date(v*1000);
20057                         }
20058                         return Date.parseDate(v, dateFormat);
20059                     }
20060                     var parsed = Date.parse(v);
20061                     return parsed ? new Date(parsed) : null;
20062                 };
20063              break;
20064             
20065         }
20066         this.convert = cv;
20067     }
20068 };
20069
20070 Roo.data.Field.prototype = {
20071     dateFormat: null,
20072     defaultValue: "",
20073     mapping: null,
20074     sortType : null,
20075     sortDir : "ASC"
20076 };/*
20077  * Based on:
20078  * Ext JS Library 1.1.1
20079  * Copyright(c) 2006-2007, Ext JS, LLC.
20080  *
20081  * Originally Released Under LGPL - original licence link has changed is not relivant.
20082  *
20083  * Fork - LGPL
20084  * <script type="text/javascript">
20085  */
20086  
20087 // Base class for reading structured data from a data source.  This class is intended to be
20088 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20089
20090 /**
20091  * @class Roo.data.DataReader
20092  * Base class for reading structured data from a data source.  This class is intended to be
20093  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20094  */
20095
20096 Roo.data.DataReader = function(meta, recordType){
20097     
20098     this.meta = meta;
20099     
20100     this.recordType = recordType instanceof Array ? 
20101         Roo.data.Record.create(recordType) : recordType;
20102 };
20103
20104 Roo.data.DataReader.prototype = {
20105      /**
20106      * Create an empty record
20107      * @param {Object} data (optional) - overlay some values
20108      * @return {Roo.data.Record} record created.
20109      */
20110     newRow :  function(d) {
20111         var da =  {};
20112         this.recordType.prototype.fields.each(function(c) {
20113             switch( c.type) {
20114                 case 'int' : da[c.name] = 0; break;
20115                 case 'date' : da[c.name] = new Date(); break;
20116                 case 'float' : da[c.name] = 0.0; break;
20117                 case 'boolean' : da[c.name] = false; break;
20118                 default : da[c.name] = ""; break;
20119             }
20120             
20121         });
20122         return new this.recordType(Roo.apply(da, d));
20123     }
20124     
20125 };/*
20126  * Based on:
20127  * Ext JS Library 1.1.1
20128  * Copyright(c) 2006-2007, Ext JS, LLC.
20129  *
20130  * Originally Released Under LGPL - original licence link has changed is not relivant.
20131  *
20132  * Fork - LGPL
20133  * <script type="text/javascript">
20134  */
20135
20136 /**
20137  * @class Roo.data.DataProxy
20138  * @extends Roo.data.Observable
20139  * This class is an abstract base class for implementations which provide retrieval of
20140  * unformatted data objects.<br>
20141  * <p>
20142  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20143  * (of the appropriate type which knows how to parse the data object) to provide a block of
20144  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20145  * <p>
20146  * Custom implementations must implement the load method as described in
20147  * {@link Roo.data.HttpProxy#load}.
20148  */
20149 Roo.data.DataProxy = function(){
20150     this.addEvents({
20151         /**
20152          * @event beforeload
20153          * Fires before a network request is made to retrieve a data object.
20154          * @param {Object} This DataProxy object.
20155          * @param {Object} params The params parameter to the load function.
20156          */
20157         beforeload : true,
20158         /**
20159          * @event load
20160          * Fires before the load method's callback is called.
20161          * @param {Object} This DataProxy object.
20162          * @param {Object} o The data object.
20163          * @param {Object} arg The callback argument object passed to the load function.
20164          */
20165         load : true,
20166         /**
20167          * @event loadexception
20168          * Fires if an Exception occurs during data retrieval.
20169          * @param {Object} This DataProxy object.
20170          * @param {Object} o The data object.
20171          * @param {Object} arg The callback argument object passed to the load function.
20172          * @param {Object} e The Exception.
20173          */
20174         loadexception : true
20175     });
20176     Roo.data.DataProxy.superclass.constructor.call(this);
20177 };
20178
20179 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20180
20181     /**
20182      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20183      */
20184 /*
20185  * Based on:
20186  * Ext JS Library 1.1.1
20187  * Copyright(c) 2006-2007, Ext JS, LLC.
20188  *
20189  * Originally Released Under LGPL - original licence link has changed is not relivant.
20190  *
20191  * Fork - LGPL
20192  * <script type="text/javascript">
20193  */
20194 /**
20195  * @class Roo.data.MemoryProxy
20196  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20197  * to the Reader when its load method is called.
20198  * @constructor
20199  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20200  */
20201 Roo.data.MemoryProxy = function(data){
20202     if (data.data) {
20203         data = data.data;
20204     }
20205     Roo.data.MemoryProxy.superclass.constructor.call(this);
20206     this.data = data;
20207 };
20208
20209 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20210     /**
20211      * Load data from the requested source (in this case an in-memory
20212      * data object passed to the constructor), read the data object into
20213      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20214      * process that block using the passed callback.
20215      * @param {Object} params This parameter is not used by the MemoryProxy class.
20216      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20217      * object into a block of Roo.data.Records.
20218      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20219      * The function must be passed <ul>
20220      * <li>The Record block object</li>
20221      * <li>The "arg" argument from the load function</li>
20222      * <li>A boolean success indicator</li>
20223      * </ul>
20224      * @param {Object} scope The scope in which to call the callback
20225      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20226      */
20227     load : function(params, reader, callback, scope, arg){
20228         params = params || {};
20229         var result;
20230         try {
20231             result = reader.readRecords(this.data);
20232         }catch(e){
20233             this.fireEvent("loadexception", this, arg, null, e);
20234             callback.call(scope, null, arg, false);
20235             return;
20236         }
20237         callback.call(scope, result, arg, true);
20238     },
20239     
20240     // private
20241     update : function(params, records){
20242         
20243     }
20244 });/*
20245  * Based on:
20246  * Ext JS Library 1.1.1
20247  * Copyright(c) 2006-2007, Ext JS, LLC.
20248  *
20249  * Originally Released Under LGPL - original licence link has changed is not relivant.
20250  *
20251  * Fork - LGPL
20252  * <script type="text/javascript">
20253  */
20254 /**
20255  * @class Roo.data.HttpProxy
20256  * @extends Roo.data.DataProxy
20257  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20258  * configured to reference a certain URL.<br><br>
20259  * <p>
20260  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20261  * from which the running page was served.<br><br>
20262  * <p>
20263  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20264  * <p>
20265  * Be aware that to enable the browser to parse an XML document, the server must set
20266  * the Content-Type header in the HTTP response to "text/xml".
20267  * @constructor
20268  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20269  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20270  * will be used to make the request.
20271  */
20272 Roo.data.HttpProxy = function(conn){
20273     Roo.data.HttpProxy.superclass.constructor.call(this);
20274     // is conn a conn config or a real conn?
20275     this.conn = conn;
20276     this.useAjax = !conn || !conn.events;
20277   
20278 };
20279
20280 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20281     // thse are take from connection...
20282     
20283     /**
20284      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20285      */
20286     /**
20287      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20288      * extra parameters to each request made by this object. (defaults to undefined)
20289      */
20290     /**
20291      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20292      *  to each request made by this object. (defaults to undefined)
20293      */
20294     /**
20295      * @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)
20296      */
20297     /**
20298      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20299      */
20300      /**
20301      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20302      * @type Boolean
20303      */
20304   
20305
20306     /**
20307      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20308      * @type Boolean
20309      */
20310     /**
20311      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20312      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20313      * a finer-grained basis than the DataProxy events.
20314      */
20315     getConnection : function(){
20316         return this.useAjax ? Roo.Ajax : this.conn;
20317     },
20318
20319     /**
20320      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20321      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20322      * process that block using the passed callback.
20323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20324      * for the request to the remote server.
20325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20326      * object into a block of Roo.data.Records.
20327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20328      * The function must be passed <ul>
20329      * <li>The Record block object</li>
20330      * <li>The "arg" argument from the load function</li>
20331      * <li>A boolean success indicator</li>
20332      * </ul>
20333      * @param {Object} scope The scope in which to call the callback
20334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20335      */
20336     load : function(params, reader, callback, scope, arg){
20337         if(this.fireEvent("beforeload", this, params) !== false){
20338             var  o = {
20339                 params : params || {},
20340                 request: {
20341                     callback : callback,
20342                     scope : scope,
20343                     arg : arg
20344                 },
20345                 reader: reader,
20346                 callback : this.loadResponse,
20347                 scope: this
20348             };
20349             if(this.useAjax){
20350                 Roo.applyIf(o, this.conn);
20351                 if(this.activeRequest){
20352                     Roo.Ajax.abort(this.activeRequest);
20353                 }
20354                 this.activeRequest = Roo.Ajax.request(o);
20355             }else{
20356                 this.conn.request(o);
20357             }
20358         }else{
20359             callback.call(scope||this, null, arg, false);
20360         }
20361     },
20362
20363     // private
20364     loadResponse : function(o, success, response){
20365         delete this.activeRequest;
20366         if(!success){
20367             this.fireEvent("loadexception", this, o, response);
20368             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20369             return;
20370         }
20371         var result;
20372         try {
20373             result = o.reader.read(response);
20374         }catch(e){
20375             this.fireEvent("loadexception", this, o, response, e);
20376             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20377             return;
20378         }
20379         
20380         this.fireEvent("load", this, o, o.request.arg);
20381         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20382     },
20383
20384     // private
20385     update : function(dataSet){
20386
20387     },
20388
20389     // private
20390     updateResponse : function(dataSet){
20391
20392     }
20393 });/*
20394  * Based on:
20395  * Ext JS Library 1.1.1
20396  * Copyright(c) 2006-2007, Ext JS, LLC.
20397  *
20398  * Originally Released Under LGPL - original licence link has changed is not relivant.
20399  *
20400  * Fork - LGPL
20401  * <script type="text/javascript">
20402  */
20403
20404 /**
20405  * @class Roo.data.ScriptTagProxy
20406  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20407  * other than the originating domain of the running page.<br><br>
20408  * <p>
20409  * <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
20410  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20411  * <p>
20412  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20413  * source code that is used as the source inside a &lt;script> tag.<br><br>
20414  * <p>
20415  * In order for the browser to process the returned data, the server must wrap the data object
20416  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20417  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20418  * depending on whether the callback name was passed:
20419  * <p>
20420  * <pre><code>
20421 boolean scriptTag = false;
20422 String cb = request.getParameter("callback");
20423 if (cb != null) {
20424     scriptTag = true;
20425     response.setContentType("text/javascript");
20426 } else {
20427     response.setContentType("application/x-json");
20428 }
20429 Writer out = response.getWriter();
20430 if (scriptTag) {
20431     out.write(cb + "(");
20432 }
20433 out.print(dataBlock.toJsonString());
20434 if (scriptTag) {
20435     out.write(");");
20436 }
20437 </pre></code>
20438  *
20439  * @constructor
20440  * @param {Object} config A configuration object.
20441  */
20442 Roo.data.ScriptTagProxy = function(config){
20443     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20444     Roo.apply(this, config);
20445     this.head = document.getElementsByTagName("head")[0];
20446 };
20447
20448 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20449
20450 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20451     /**
20452      * @cfg {String} url The URL from which to request the data object.
20453      */
20454     /**
20455      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20456      */
20457     timeout : 30000,
20458     /**
20459      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20460      * the server the name of the callback function set up by the load call to process the returned data object.
20461      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20462      * javascript output which calls this named function passing the data object as its only parameter.
20463      */
20464     callbackParam : "callback",
20465     /**
20466      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20467      * name to the request.
20468      */
20469     nocache : true,
20470
20471     /**
20472      * Load data from the configured URL, read the data object into
20473      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20474      * process that block using the passed callback.
20475      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20476      * for the request to the remote server.
20477      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20478      * object into a block of Roo.data.Records.
20479      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20480      * The function must be passed <ul>
20481      * <li>The Record block object</li>
20482      * <li>The "arg" argument from the load function</li>
20483      * <li>A boolean success indicator</li>
20484      * </ul>
20485      * @param {Object} scope The scope in which to call the callback
20486      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20487      */
20488     load : function(params, reader, callback, scope, arg){
20489         if(this.fireEvent("beforeload", this, params) !== false){
20490
20491             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20492
20493             var url = this.url;
20494             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20495             if(this.nocache){
20496                 url += "&_dc=" + (new Date().getTime());
20497             }
20498             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20499             var trans = {
20500                 id : transId,
20501                 cb : "stcCallback"+transId,
20502                 scriptId : "stcScript"+transId,
20503                 params : params,
20504                 arg : arg,
20505                 url : url,
20506                 callback : callback,
20507                 scope : scope,
20508                 reader : reader
20509             };
20510             var conn = this;
20511
20512             window[trans.cb] = function(o){
20513                 conn.handleResponse(o, trans);
20514             };
20515
20516             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20517
20518             if(this.autoAbort !== false){
20519                 this.abort();
20520             }
20521
20522             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20523
20524             var script = document.createElement("script");
20525             script.setAttribute("src", url);
20526             script.setAttribute("type", "text/javascript");
20527             script.setAttribute("id", trans.scriptId);
20528             this.head.appendChild(script);
20529
20530             this.trans = trans;
20531         }else{
20532             callback.call(scope||this, null, arg, false);
20533         }
20534     },
20535
20536     // private
20537     isLoading : function(){
20538         return this.trans ? true : false;
20539     },
20540
20541     /**
20542      * Abort the current server request.
20543      */
20544     abort : function(){
20545         if(this.isLoading()){
20546             this.destroyTrans(this.trans);
20547         }
20548     },
20549
20550     // private
20551     destroyTrans : function(trans, isLoaded){
20552         this.head.removeChild(document.getElementById(trans.scriptId));
20553         clearTimeout(trans.timeoutId);
20554         if(isLoaded){
20555             window[trans.cb] = undefined;
20556             try{
20557                 delete window[trans.cb];
20558             }catch(e){}
20559         }else{
20560             // if hasn't been loaded, wait for load to remove it to prevent script error
20561             window[trans.cb] = function(){
20562                 window[trans.cb] = undefined;
20563                 try{
20564                     delete window[trans.cb];
20565                 }catch(e){}
20566             };
20567         }
20568     },
20569
20570     // private
20571     handleResponse : function(o, trans){
20572         this.trans = false;
20573         this.destroyTrans(trans, true);
20574         var result;
20575         try {
20576             result = trans.reader.readRecords(o);
20577         }catch(e){
20578             this.fireEvent("loadexception", this, o, trans.arg, e);
20579             trans.callback.call(trans.scope||window, null, trans.arg, false);
20580             return;
20581         }
20582         this.fireEvent("load", this, o, trans.arg);
20583         trans.callback.call(trans.scope||window, result, trans.arg, true);
20584     },
20585
20586     // private
20587     handleFailure : function(trans){
20588         this.trans = false;
20589         this.destroyTrans(trans, false);
20590         this.fireEvent("loadexception", this, null, trans.arg);
20591         trans.callback.call(trans.scope||window, null, trans.arg, false);
20592     }
20593 });/*
20594  * Based on:
20595  * Ext JS Library 1.1.1
20596  * Copyright(c) 2006-2007, Ext JS, LLC.
20597  *
20598  * Originally Released Under LGPL - original licence link has changed is not relivant.
20599  *
20600  * Fork - LGPL
20601  * <script type="text/javascript">
20602  */
20603
20604 /**
20605  * @class Roo.data.JsonReader
20606  * @extends Roo.data.DataReader
20607  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20608  * based on mappings in a provided Roo.data.Record constructor.
20609  * 
20610  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20611  * in the reply previously. 
20612  * 
20613  * <p>
20614  * Example code:
20615  * <pre><code>
20616 var RecordDef = Roo.data.Record.create([
20617     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20618     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20619 ]);
20620 var myReader = new Roo.data.JsonReader({
20621     totalProperty: "results",    // The property which contains the total dataset size (optional)
20622     root: "rows",                // The property which contains an Array of row objects
20623     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20624 }, RecordDef);
20625 </code></pre>
20626  * <p>
20627  * This would consume a JSON file like this:
20628  * <pre><code>
20629 { 'results': 2, 'rows': [
20630     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20631     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20632 }
20633 </code></pre>
20634  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20635  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20636  * paged from the remote server.
20637  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20638  * @cfg {String} root name of the property which contains the Array of row objects.
20639  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20640  * @constructor
20641  * Create a new JsonReader
20642  * @param {Object} meta Metadata configuration options
20643  * @param {Object} recordType Either an Array of field definition objects,
20644  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20645  */
20646 Roo.data.JsonReader = function(meta, recordType){
20647     
20648     meta = meta || {};
20649     // set some defaults:
20650     Roo.applyIf(meta, {
20651         totalProperty: 'total',
20652         successProperty : 'success',
20653         root : 'data',
20654         id : 'id'
20655     });
20656     
20657     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20658 };
20659 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20660     
20661     /**
20662      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20663      * Used by Store query builder to append _requestMeta to params.
20664      * 
20665      */
20666     metaFromRemote : false,
20667     /**
20668      * This method is only used by a DataProxy which has retrieved data from a remote server.
20669      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20670      * @return {Object} data A data block which is used by an Roo.data.Store object as
20671      * a cache of Roo.data.Records.
20672      */
20673     read : function(response){
20674         var json = response.responseText;
20675        
20676         var o = /* eval:var:o */ eval("("+json+")");
20677         if(!o) {
20678             throw {message: "JsonReader.read: Json object not found"};
20679         }
20680         
20681         if(o.metaData){
20682             
20683             delete this.ef;
20684             this.metaFromRemote = true;
20685             this.meta = o.metaData;
20686             this.recordType = Roo.data.Record.create(o.metaData.fields);
20687             this.onMetaChange(this.meta, this.recordType, o);
20688         }
20689         return this.readRecords(o);
20690     },
20691
20692     // private function a store will implement
20693     onMetaChange : function(meta, recordType, o){
20694
20695     },
20696
20697     /**
20698          * @ignore
20699          */
20700     simpleAccess: function(obj, subsc) {
20701         return obj[subsc];
20702     },
20703
20704         /**
20705          * @ignore
20706          */
20707     getJsonAccessor: function(){
20708         var re = /[\[\.]/;
20709         return function(expr) {
20710             try {
20711                 return(re.test(expr))
20712                     ? new Function("obj", "return obj." + expr)
20713                     : function(obj){
20714                         return obj[expr];
20715                     };
20716             } catch(e){}
20717             return Roo.emptyFn;
20718         };
20719     }(),
20720
20721     /**
20722      * Create a data block containing Roo.data.Records from an XML document.
20723      * @param {Object} o An object which contains an Array of row objects in the property specified
20724      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20725      * which contains the total size of the dataset.
20726      * @return {Object} data A data block which is used by an Roo.data.Store object as
20727      * a cache of Roo.data.Records.
20728      */
20729     readRecords : function(o){
20730         /**
20731          * After any data loads, the raw JSON data is available for further custom processing.
20732          * @type Object
20733          */
20734         this.jsonData = o;
20735         var s = this.meta, Record = this.recordType,
20736             f = Record.prototype.fields, fi = f.items, fl = f.length;
20737
20738 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20739         if (!this.ef) {
20740             if(s.totalProperty) {
20741                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20742                 }
20743                 if(s.successProperty) {
20744                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20745                 }
20746                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20747                 if (s.id) {
20748                         var g = this.getJsonAccessor(s.id);
20749                         this.getId = function(rec) {
20750                                 var r = g(rec);
20751                                 return (r === undefined || r === "") ? null : r;
20752                         };
20753                 } else {
20754                         this.getId = function(){return null;};
20755                 }
20756             this.ef = [];
20757             for(var jj = 0; jj < fl; jj++){
20758                 f = fi[jj];
20759                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20760                 this.ef[jj] = this.getJsonAccessor(map);
20761             }
20762         }
20763
20764         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20765         if(s.totalProperty){
20766             var vt = parseInt(this.getTotal(o), 10);
20767             if(!isNaN(vt)){
20768                 totalRecords = vt;
20769             }
20770         }
20771         if(s.successProperty){
20772             var vs = this.getSuccess(o);
20773             if(vs === false || vs === 'false'){
20774                 success = false;
20775             }
20776         }
20777         var records = [];
20778             for(var i = 0; i < c; i++){
20779                     var n = root[i];
20780                 var values = {};
20781                 var id = this.getId(n);
20782                 for(var j = 0; j < fl; j++){
20783                     f = fi[j];
20784                 var v = this.ef[j](n);
20785                 if (!f.convert) {
20786                     Roo.log('missing convert for ' + f.name);
20787                     Roo.log(f);
20788                     continue;
20789                 }
20790                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20791                 }
20792                 var record = new Record(values, id);
20793                 record.json = n;
20794                 records[i] = record;
20795             }
20796             return {
20797                 success : success,
20798                 records : records,
20799                 totalRecords : totalRecords
20800             };
20801     }
20802 });/*
20803  * Based on:
20804  * Ext JS Library 1.1.1
20805  * Copyright(c) 2006-2007, Ext JS, LLC.
20806  *
20807  * Originally Released Under LGPL - original licence link has changed is not relivant.
20808  *
20809  * Fork - LGPL
20810  * <script type="text/javascript">
20811  */
20812
20813 /**
20814  * @class Roo.data.XmlReader
20815  * @extends Roo.data.DataReader
20816  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20817  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20818  * <p>
20819  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20820  * header in the HTTP response must be set to "text/xml".</em>
20821  * <p>
20822  * Example code:
20823  * <pre><code>
20824 var RecordDef = Roo.data.Record.create([
20825    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20826    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20827 ]);
20828 var myReader = new Roo.data.XmlReader({
20829    totalRecords: "results", // The element which contains the total dataset size (optional)
20830    record: "row",           // The repeated element which contains row information
20831    id: "id"                 // The element within the row that provides an ID for the record (optional)
20832 }, RecordDef);
20833 </code></pre>
20834  * <p>
20835  * This would consume an XML file like this:
20836  * <pre><code>
20837 &lt;?xml?>
20838 &lt;dataset>
20839  &lt;results>2&lt;/results>
20840  &lt;row>
20841    &lt;id>1&lt;/id>
20842    &lt;name>Bill&lt;/name>
20843    &lt;occupation>Gardener&lt;/occupation>
20844  &lt;/row>
20845  &lt;row>
20846    &lt;id>2&lt;/id>
20847    &lt;name>Ben&lt;/name>
20848    &lt;occupation>Horticulturalist&lt;/occupation>
20849  &lt;/row>
20850 &lt;/dataset>
20851 </code></pre>
20852  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20854  * paged from the remote server.
20855  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20856  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20857  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20858  * a record identifier value.
20859  * @constructor
20860  * Create a new XmlReader
20861  * @param {Object} meta Metadata configuration options
20862  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20863  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20864  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20865  */
20866 Roo.data.XmlReader = function(meta, recordType){
20867     meta = meta || {};
20868     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20869 };
20870 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20871     /**
20872      * This method is only used by a DataProxy which has retrieved data from a remote server.
20873          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20874          * to contain a method called 'responseXML' that returns an XML document object.
20875      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20876      * a cache of Roo.data.Records.
20877      */
20878     read : function(response){
20879         var doc = response.responseXML;
20880         if(!doc) {
20881             throw {message: "XmlReader.read: XML Document not available"};
20882         }
20883         return this.readRecords(doc);
20884     },
20885
20886     /**
20887      * Create a data block containing Roo.data.Records from an XML document.
20888          * @param {Object} doc A parsed XML document.
20889      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20890      * a cache of Roo.data.Records.
20891      */
20892     readRecords : function(doc){
20893         /**
20894          * After any data loads/reads, the raw XML Document is available for further custom processing.
20895          * @type XMLDocument
20896          */
20897         this.xmlData = doc;
20898         var root = doc.documentElement || doc;
20899         var q = Roo.DomQuery;
20900         var recordType = this.recordType, fields = recordType.prototype.fields;
20901         var sid = this.meta.id;
20902         var totalRecords = 0, success = true;
20903         if(this.meta.totalRecords){
20904             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20905         }
20906         
20907         if(this.meta.success){
20908             var sv = q.selectValue(this.meta.success, root, true);
20909             success = sv !== false && sv !== 'false';
20910         }
20911         var records = [];
20912         var ns = q.select(this.meta.record, root);
20913         for(var i = 0, len = ns.length; i < len; i++) {
20914                 var n = ns[i];
20915                 var values = {};
20916                 var id = sid ? q.selectValue(sid, n) : undefined;
20917                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20918                     var f = fields.items[j];
20919                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20920                     v = f.convert(v);
20921                     values[f.name] = v;
20922                 }
20923                 var record = new recordType(values, id);
20924                 record.node = n;
20925                 records[records.length] = record;
20926             }
20927
20928             return {
20929                 success : success,
20930                 records : records,
20931                 totalRecords : totalRecords || records.length
20932             };
20933     }
20934 });/*
20935  * Based on:
20936  * Ext JS Library 1.1.1
20937  * Copyright(c) 2006-2007, Ext JS, LLC.
20938  *
20939  * Originally Released Under LGPL - original licence link has changed is not relivant.
20940  *
20941  * Fork - LGPL
20942  * <script type="text/javascript">
20943  */
20944
20945 /**
20946  * @class Roo.data.ArrayReader
20947  * @extends Roo.data.DataReader
20948  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20949  * Each element of that Array represents a row of data fields. The
20950  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20951  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20952  * <p>
20953  * Example code:.
20954  * <pre><code>
20955 var RecordDef = Roo.data.Record.create([
20956     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20957     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20958 ]);
20959 var myReader = new Roo.data.ArrayReader({
20960     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20961 }, RecordDef);
20962 </code></pre>
20963  * <p>
20964  * This would consume an Array like this:
20965  * <pre><code>
20966 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20967   </code></pre>
20968  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20969  * @constructor
20970  * Create a new JsonReader
20971  * @param {Object} meta Metadata configuration options.
20972  * @param {Object} recordType Either an Array of field definition objects
20973  * as specified to {@link Roo.data.Record#create},
20974  * or an {@link Roo.data.Record} object
20975  * created using {@link Roo.data.Record#create}.
20976  */
20977 Roo.data.ArrayReader = function(meta, recordType){
20978     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20979 };
20980
20981 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20982     /**
20983      * Create a data block containing Roo.data.Records from an XML document.
20984      * @param {Object} o An Array of row objects which represents the dataset.
20985      * @return {Object} data A data block which is used by an Roo.data.Store object as
20986      * a cache of Roo.data.Records.
20987      */
20988     readRecords : function(o){
20989         var sid = this.meta ? this.meta.id : null;
20990         var recordType = this.recordType, fields = recordType.prototype.fields;
20991         var records = [];
20992         var root = o;
20993             for(var i = 0; i < root.length; i++){
20994                     var n = root[i];
20995                 var values = {};
20996                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20997                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20998                 var f = fields.items[j];
20999                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21000                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21001                 v = f.convert(v);
21002                 values[f.name] = v;
21003             }
21004                 var record = new recordType(values, id);
21005                 record.json = n;
21006                 records[records.length] = record;
21007             }
21008             return {
21009                 records : records,
21010                 totalRecords : records.length
21011             };
21012     }
21013 });/*
21014  * Based on:
21015  * Ext JS Library 1.1.1
21016  * Copyright(c) 2006-2007, Ext JS, LLC.
21017  *
21018  * Originally Released Under LGPL - original licence link has changed is not relivant.
21019  *
21020  * Fork - LGPL
21021  * <script type="text/javascript">
21022  */
21023
21024
21025 /**
21026  * @class Roo.data.Tree
21027  * @extends Roo.util.Observable
21028  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21029  * in the tree have most standard DOM functionality.
21030  * @constructor
21031  * @param {Node} root (optional) The root node
21032  */
21033 Roo.data.Tree = function(root){
21034    this.nodeHash = {};
21035    /**
21036     * The root node for this tree
21037     * @type Node
21038     */
21039    this.root = null;
21040    if(root){
21041        this.setRootNode(root);
21042    }
21043    this.addEvents({
21044        /**
21045         * @event append
21046         * Fires when a new child node is appended to a node in this tree.
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} parent The parent node
21049         * @param {Node} node The newly appended node
21050         * @param {Number} index The index of the newly appended node
21051         */
21052        "append" : true,
21053        /**
21054         * @event remove
21055         * Fires when a child node is removed from a node in this tree.
21056         * @param {Tree} tree The owner tree
21057         * @param {Node} parent The parent node
21058         * @param {Node} node The child node removed
21059         */
21060        "remove" : true,
21061        /**
21062         * @event move
21063         * Fires when a node is moved to a new location in the tree
21064         * @param {Tree} tree The owner tree
21065         * @param {Node} node The node moved
21066         * @param {Node} oldParent The old parent of this node
21067         * @param {Node} newParent The new parent of this node
21068         * @param {Number} index The index it was moved to
21069         */
21070        "move" : true,
21071        /**
21072         * @event insert
21073         * Fires when a new child node is inserted in a node in this tree.
21074         * @param {Tree} tree The owner tree
21075         * @param {Node} parent The parent node
21076         * @param {Node} node The child node inserted
21077         * @param {Node} refNode The child node the node was inserted before
21078         */
21079        "insert" : true,
21080        /**
21081         * @event beforeappend
21082         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21083         * @param {Tree} tree The owner tree
21084         * @param {Node} parent The parent node
21085         * @param {Node} node The child node to be appended
21086         */
21087        "beforeappend" : true,
21088        /**
21089         * @event beforeremove
21090         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21091         * @param {Tree} tree The owner tree
21092         * @param {Node} parent The parent node
21093         * @param {Node} node The child node to be removed
21094         */
21095        "beforeremove" : true,
21096        /**
21097         * @event beforemove
21098         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21099         * @param {Tree} tree The owner tree
21100         * @param {Node} node The node being moved
21101         * @param {Node} oldParent The parent of the node
21102         * @param {Node} newParent The new parent the node is moving to
21103         * @param {Number} index The index it is being moved to
21104         */
21105        "beforemove" : true,
21106        /**
21107         * @event beforeinsert
21108         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21109         * @param {Tree} tree The owner tree
21110         * @param {Node} parent The parent node
21111         * @param {Node} node The child node to be inserted
21112         * @param {Node} refNode The child node the node is being inserted before
21113         */
21114        "beforeinsert" : true
21115    });
21116
21117     Roo.data.Tree.superclass.constructor.call(this);
21118 };
21119
21120 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21121     pathSeparator: "/",
21122
21123     proxyNodeEvent : function(){
21124         return this.fireEvent.apply(this, arguments);
21125     },
21126
21127     /**
21128      * Returns the root node for this tree.
21129      * @return {Node}
21130      */
21131     getRootNode : function(){
21132         return this.root;
21133     },
21134
21135     /**
21136      * Sets the root node for this tree.
21137      * @param {Node} node
21138      * @return {Node}
21139      */
21140     setRootNode : function(node){
21141         this.root = node;
21142         node.ownerTree = this;
21143         node.isRoot = true;
21144         this.registerNode(node);
21145         return node;
21146     },
21147
21148     /**
21149      * Gets a node in this tree by its id.
21150      * @param {String} id
21151      * @return {Node}
21152      */
21153     getNodeById : function(id){
21154         return this.nodeHash[id];
21155     },
21156
21157     registerNode : function(node){
21158         this.nodeHash[node.id] = node;
21159     },
21160
21161     unregisterNode : function(node){
21162         delete this.nodeHash[node.id];
21163     },
21164
21165     toString : function(){
21166         return "[Tree"+(this.id?" "+this.id:"")+"]";
21167     }
21168 });
21169
21170 /**
21171  * @class Roo.data.Node
21172  * @extends Roo.util.Observable
21173  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21174  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21175  * @constructor
21176  * @param {Object} attributes The attributes/config for the node
21177  */
21178 Roo.data.Node = function(attributes){
21179     /**
21180      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21181      * @type {Object}
21182      */
21183     this.attributes = attributes || {};
21184     this.leaf = this.attributes.leaf;
21185     /**
21186      * The node id. @type String
21187      */
21188     this.id = this.attributes.id;
21189     if(!this.id){
21190         this.id = Roo.id(null, "ynode-");
21191         this.attributes.id = this.id;
21192     }
21193      
21194     
21195     /**
21196      * All child nodes of this node. @type Array
21197      */
21198     this.childNodes = [];
21199     if(!this.childNodes.indexOf){ // indexOf is a must
21200         this.childNodes.indexOf = function(o){
21201             for(var i = 0, len = this.length; i < len; i++){
21202                 if(this[i] == o) {
21203                     return i;
21204                 }
21205             }
21206             return -1;
21207         };
21208     }
21209     /**
21210      * The parent node for this node. @type Node
21211      */
21212     this.parentNode = null;
21213     /**
21214      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21215      */
21216     this.firstChild = null;
21217     /**
21218      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21219      */
21220     this.lastChild = null;
21221     /**
21222      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21223      */
21224     this.previousSibling = null;
21225     /**
21226      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21227      */
21228     this.nextSibling = null;
21229
21230     this.addEvents({
21231        /**
21232         * @event append
21233         * Fires when a new child node is appended
21234         * @param {Tree} tree The owner tree
21235         * @param {Node} this This node
21236         * @param {Node} node The newly appended node
21237         * @param {Number} index The index of the newly appended node
21238         */
21239        "append" : true,
21240        /**
21241         * @event remove
21242         * Fires when a child node is removed
21243         * @param {Tree} tree The owner tree
21244         * @param {Node} this This node
21245         * @param {Node} node The removed node
21246         */
21247        "remove" : true,
21248        /**
21249         * @event move
21250         * Fires when this node is moved to a new location in the tree
21251         * @param {Tree} tree The owner tree
21252         * @param {Node} this This node
21253         * @param {Node} oldParent The old parent of this node
21254         * @param {Node} newParent The new parent of this node
21255         * @param {Number} index The index it was moved to
21256         */
21257        "move" : true,
21258        /**
21259         * @event insert
21260         * Fires when a new child node is inserted.
21261         * @param {Tree} tree The owner tree
21262         * @param {Node} this This node
21263         * @param {Node} node The child node inserted
21264         * @param {Node} refNode The child node the node was inserted before
21265         */
21266        "insert" : true,
21267        /**
21268         * @event beforeappend
21269         * Fires before a new child is appended, return false to cancel the append.
21270         * @param {Tree} tree The owner tree
21271         * @param {Node} this This node
21272         * @param {Node} node The child node to be appended
21273         */
21274        "beforeappend" : true,
21275        /**
21276         * @event beforeremove
21277         * Fires before a child is removed, return false to cancel the remove.
21278         * @param {Tree} tree The owner tree
21279         * @param {Node} this This node
21280         * @param {Node} node The child node to be removed
21281         */
21282        "beforeremove" : true,
21283        /**
21284         * @event beforemove
21285         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21286         * @param {Tree} tree The owner tree
21287         * @param {Node} this This node
21288         * @param {Node} oldParent The parent of this node
21289         * @param {Node} newParent The new parent this node is moving to
21290         * @param {Number} index The index it is being moved to
21291         */
21292        "beforemove" : true,
21293        /**
21294         * @event beforeinsert
21295         * Fires before a new child is inserted, return false to cancel the insert.
21296         * @param {Tree} tree The owner tree
21297         * @param {Node} this This node
21298         * @param {Node} node The child node to be inserted
21299         * @param {Node} refNode The child node the node is being inserted before
21300         */
21301        "beforeinsert" : true
21302    });
21303     this.listeners = this.attributes.listeners;
21304     Roo.data.Node.superclass.constructor.call(this);
21305 };
21306
21307 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21308     fireEvent : function(evtName){
21309         // first do standard event for this node
21310         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21311             return false;
21312         }
21313         // then bubble it up to the tree if the event wasn't cancelled
21314         var ot = this.getOwnerTree();
21315         if(ot){
21316             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21317                 return false;
21318             }
21319         }
21320         return true;
21321     },
21322
21323     /**
21324      * Returns true if this node is a leaf
21325      * @return {Boolean}
21326      */
21327     isLeaf : function(){
21328         return this.leaf === true;
21329     },
21330
21331     // private
21332     setFirstChild : function(node){
21333         this.firstChild = node;
21334     },
21335
21336     //private
21337     setLastChild : function(node){
21338         this.lastChild = node;
21339     },
21340
21341
21342     /**
21343      * Returns true if this node is the last child of its parent
21344      * @return {Boolean}
21345      */
21346     isLast : function(){
21347        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21348     },
21349
21350     /**
21351      * Returns true if this node is the first child of its parent
21352      * @return {Boolean}
21353      */
21354     isFirst : function(){
21355        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21356     },
21357
21358     hasChildNodes : function(){
21359         return !this.isLeaf() && this.childNodes.length > 0;
21360     },
21361
21362     /**
21363      * Insert node(s) as the last child node of this node.
21364      * @param {Node/Array} node The node or Array of nodes to append
21365      * @return {Node} The appended node if single append, or null if an array was passed
21366      */
21367     appendChild : function(node){
21368         var multi = false;
21369         if(node instanceof Array){
21370             multi = node;
21371         }else if(arguments.length > 1){
21372             multi = arguments;
21373         }
21374         // if passed an array or multiple args do them one by one
21375         if(multi){
21376             for(var i = 0, len = multi.length; i < len; i++) {
21377                 this.appendChild(multi[i]);
21378             }
21379         }else{
21380             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21381                 return false;
21382             }
21383             var index = this.childNodes.length;
21384             var oldParent = node.parentNode;
21385             // it's a move, make sure we move it cleanly
21386             if(oldParent){
21387                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21388                     return false;
21389                 }
21390                 oldParent.removeChild(node);
21391             }
21392             index = this.childNodes.length;
21393             if(index == 0){
21394                 this.setFirstChild(node);
21395             }
21396             this.childNodes.push(node);
21397             node.parentNode = this;
21398             var ps = this.childNodes[index-1];
21399             if(ps){
21400                 node.previousSibling = ps;
21401                 ps.nextSibling = node;
21402             }else{
21403                 node.previousSibling = null;
21404             }
21405             node.nextSibling = null;
21406             this.setLastChild(node);
21407             node.setOwnerTree(this.getOwnerTree());
21408             this.fireEvent("append", this.ownerTree, this, node, index);
21409             if(oldParent){
21410                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21411             }
21412             return node;
21413         }
21414     },
21415
21416     /**
21417      * Removes a child node from this node.
21418      * @param {Node} node The node to remove
21419      * @return {Node} The removed node
21420      */
21421     removeChild : function(node){
21422         var index = this.childNodes.indexOf(node);
21423         if(index == -1){
21424             return false;
21425         }
21426         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21427             return false;
21428         }
21429
21430         // remove it from childNodes collection
21431         this.childNodes.splice(index, 1);
21432
21433         // update siblings
21434         if(node.previousSibling){
21435             node.previousSibling.nextSibling = node.nextSibling;
21436         }
21437         if(node.nextSibling){
21438             node.nextSibling.previousSibling = node.previousSibling;
21439         }
21440
21441         // update child refs
21442         if(this.firstChild == node){
21443             this.setFirstChild(node.nextSibling);
21444         }
21445         if(this.lastChild == node){
21446             this.setLastChild(node.previousSibling);
21447         }
21448
21449         node.setOwnerTree(null);
21450         // clear any references from the node
21451         node.parentNode = null;
21452         node.previousSibling = null;
21453         node.nextSibling = null;
21454         this.fireEvent("remove", this.ownerTree, this, node);
21455         return node;
21456     },
21457
21458     /**
21459      * Inserts the first node before the second node in this nodes childNodes collection.
21460      * @param {Node} node The node to insert
21461      * @param {Node} refNode The node to insert before (if null the node is appended)
21462      * @return {Node} The inserted node
21463      */
21464     insertBefore : function(node, refNode){
21465         if(!refNode){ // like standard Dom, refNode can be null for append
21466             return this.appendChild(node);
21467         }
21468         // nothing to do
21469         if(node == refNode){
21470             return false;
21471         }
21472
21473         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21474             return false;
21475         }
21476         var index = this.childNodes.indexOf(refNode);
21477         var oldParent = node.parentNode;
21478         var refIndex = index;
21479
21480         // when moving internally, indexes will change after remove
21481         if(oldParent == this && this.childNodes.indexOf(node) < index){
21482             refIndex--;
21483         }
21484
21485         // it's a move, make sure we move it cleanly
21486         if(oldParent){
21487             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21488                 return false;
21489             }
21490             oldParent.removeChild(node);
21491         }
21492         if(refIndex == 0){
21493             this.setFirstChild(node);
21494         }
21495         this.childNodes.splice(refIndex, 0, node);
21496         node.parentNode = this;
21497         var ps = this.childNodes[refIndex-1];
21498         if(ps){
21499             node.previousSibling = ps;
21500             ps.nextSibling = node;
21501         }else{
21502             node.previousSibling = null;
21503         }
21504         node.nextSibling = refNode;
21505         refNode.previousSibling = node;
21506         node.setOwnerTree(this.getOwnerTree());
21507         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21508         if(oldParent){
21509             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21510         }
21511         return node;
21512     },
21513
21514     /**
21515      * Returns the child node at the specified index.
21516      * @param {Number} index
21517      * @return {Node}
21518      */
21519     item : function(index){
21520         return this.childNodes[index];
21521     },
21522
21523     /**
21524      * Replaces one child node in this node with another.
21525      * @param {Node} newChild The replacement node
21526      * @param {Node} oldChild The node to replace
21527      * @return {Node} The replaced node
21528      */
21529     replaceChild : function(newChild, oldChild){
21530         this.insertBefore(newChild, oldChild);
21531         this.removeChild(oldChild);
21532         return oldChild;
21533     },
21534
21535     /**
21536      * Returns the index of a child node
21537      * @param {Node} node
21538      * @return {Number} The index of the node or -1 if it was not found
21539      */
21540     indexOf : function(child){
21541         return this.childNodes.indexOf(child);
21542     },
21543
21544     /**
21545      * Returns the tree this node is in.
21546      * @return {Tree}
21547      */
21548     getOwnerTree : function(){
21549         // if it doesn't have one, look for one
21550         if(!this.ownerTree){
21551             var p = this;
21552             while(p){
21553                 if(p.ownerTree){
21554                     this.ownerTree = p.ownerTree;
21555                     break;
21556                 }
21557                 p = p.parentNode;
21558             }
21559         }
21560         return this.ownerTree;
21561     },
21562
21563     /**
21564      * Returns depth of this node (the root node has a depth of 0)
21565      * @return {Number}
21566      */
21567     getDepth : function(){
21568         var depth = 0;
21569         var p = this;
21570         while(p.parentNode){
21571             ++depth;
21572             p = p.parentNode;
21573         }
21574         return depth;
21575     },
21576
21577     // private
21578     setOwnerTree : function(tree){
21579         // if it's move, we need to update everyone
21580         if(tree != this.ownerTree){
21581             if(this.ownerTree){
21582                 this.ownerTree.unregisterNode(this);
21583             }
21584             this.ownerTree = tree;
21585             var cs = this.childNodes;
21586             for(var i = 0, len = cs.length; i < len; i++) {
21587                 cs[i].setOwnerTree(tree);
21588             }
21589             if(tree){
21590                 tree.registerNode(this);
21591             }
21592         }
21593     },
21594
21595     /**
21596      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21597      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21598      * @return {String} The path
21599      */
21600     getPath : function(attr){
21601         attr = attr || "id";
21602         var p = this.parentNode;
21603         var b = [this.attributes[attr]];
21604         while(p){
21605             b.unshift(p.attributes[attr]);
21606             p = p.parentNode;
21607         }
21608         var sep = this.getOwnerTree().pathSeparator;
21609         return sep + b.join(sep);
21610     },
21611
21612     /**
21613      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21614      * function call will be the scope provided or the current node. The arguments to the function
21615      * will be the args provided or the current node. If the function returns false at any point,
21616      * the bubble is stopped.
21617      * @param {Function} fn The function to call
21618      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21619      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21620      */
21621     bubble : function(fn, scope, args){
21622         var p = this;
21623         while(p){
21624             if(fn.call(scope || p, args || p) === false){
21625                 break;
21626             }
21627             p = p.parentNode;
21628         }
21629     },
21630
21631     /**
21632      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21633      * function call will be the scope provided or the current node. The arguments to the function
21634      * will be the args provided or the current node. If the function returns false at any point,
21635      * the cascade is stopped on that branch.
21636      * @param {Function} fn The function to call
21637      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21638      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21639      */
21640     cascade : function(fn, scope, args){
21641         if(fn.call(scope || this, args || this) !== false){
21642             var cs = this.childNodes;
21643             for(var i = 0, len = cs.length; i < len; i++) {
21644                 cs[i].cascade(fn, scope, args);
21645             }
21646         }
21647     },
21648
21649     /**
21650      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21651      * function call will be the scope provided or the current node. The arguments to the function
21652      * will be the args provided or the current node. If the function returns false at any point,
21653      * the iteration stops.
21654      * @param {Function} fn The function to call
21655      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21656      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21657      */
21658     eachChild : function(fn, scope, args){
21659         var cs = this.childNodes;
21660         for(var i = 0, len = cs.length; i < len; i++) {
21661                 if(fn.call(scope || this, args || cs[i]) === false){
21662                     break;
21663                 }
21664         }
21665     },
21666
21667     /**
21668      * Finds the first child that has the attribute with the specified value.
21669      * @param {String} attribute The attribute name
21670      * @param {Mixed} value The value to search for
21671      * @return {Node} The found child or null if none was found
21672      */
21673     findChild : function(attribute, value){
21674         var cs = this.childNodes;
21675         for(var i = 0, len = cs.length; i < len; i++) {
21676                 if(cs[i].attributes[attribute] == value){
21677                     return cs[i];
21678                 }
21679         }
21680         return null;
21681     },
21682
21683     /**
21684      * Finds the first child by a custom function. The child matches if the function passed
21685      * returns true.
21686      * @param {Function} fn
21687      * @param {Object} scope (optional)
21688      * @return {Node} The found child or null if none was found
21689      */
21690     findChildBy : function(fn, scope){
21691         var cs = this.childNodes;
21692         for(var i = 0, len = cs.length; i < len; i++) {
21693                 if(fn.call(scope||cs[i], cs[i]) === true){
21694                     return cs[i];
21695                 }
21696         }
21697         return null;
21698     },
21699
21700     /**
21701      * Sorts this nodes children using the supplied sort function
21702      * @param {Function} fn
21703      * @param {Object} scope (optional)
21704      */
21705     sort : function(fn, scope){
21706         var cs = this.childNodes;
21707         var len = cs.length;
21708         if(len > 0){
21709             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21710             cs.sort(sortFn);
21711             for(var i = 0; i < len; i++){
21712                 var n = cs[i];
21713                 n.previousSibling = cs[i-1];
21714                 n.nextSibling = cs[i+1];
21715                 if(i == 0){
21716                     this.setFirstChild(n);
21717                 }
21718                 if(i == len-1){
21719                     this.setLastChild(n);
21720                 }
21721             }
21722         }
21723     },
21724
21725     /**
21726      * Returns true if this node is an ancestor (at any point) of the passed node.
21727      * @param {Node} node
21728      * @return {Boolean}
21729      */
21730     contains : function(node){
21731         return node.isAncestor(this);
21732     },
21733
21734     /**
21735      * Returns true if the passed node is an ancestor (at any point) of this node.
21736      * @param {Node} node
21737      * @return {Boolean}
21738      */
21739     isAncestor : function(node){
21740         var p = this.parentNode;
21741         while(p){
21742             if(p == node){
21743                 return true;
21744             }
21745             p = p.parentNode;
21746         }
21747         return false;
21748     },
21749
21750     toString : function(){
21751         return "[Node"+(this.id?" "+this.id:"")+"]";
21752     }
21753 });/*
21754  * Based on:
21755  * Ext JS Library 1.1.1
21756  * Copyright(c) 2006-2007, Ext JS, LLC.
21757  *
21758  * Originally Released Under LGPL - original licence link has changed is not relivant.
21759  *
21760  * Fork - LGPL
21761  * <script type="text/javascript">
21762  */
21763  
21764
21765 /**
21766  * @class Roo.ComponentMgr
21767  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21768  * @singleton
21769  */
21770 Roo.ComponentMgr = function(){
21771     var all = new Roo.util.MixedCollection();
21772
21773     return {
21774         /**
21775          * Registers a component.
21776          * @param {Roo.Component} c The component
21777          */
21778         register : function(c){
21779             all.add(c);
21780         },
21781
21782         /**
21783          * Unregisters a component.
21784          * @param {Roo.Component} c The component
21785          */
21786         unregister : function(c){
21787             all.remove(c);
21788         },
21789
21790         /**
21791          * Returns a component by id
21792          * @param {String} id The component id
21793          */
21794         get : function(id){
21795             return all.get(id);
21796         },
21797
21798         /**
21799          * Registers a function that will be called when a specified component is added to ComponentMgr
21800          * @param {String} id The component id
21801          * @param {Funtction} fn The callback function
21802          * @param {Object} scope The scope of the callback
21803          */
21804         onAvailable : function(id, fn, scope){
21805             all.on("add", function(index, o){
21806                 if(o.id == id){
21807                     fn.call(scope || o, o);
21808                     all.un("add", fn, scope);
21809                 }
21810             });
21811         }
21812     };
21813 }();/*
21814  * Based on:
21815  * Ext JS Library 1.1.1
21816  * Copyright(c) 2006-2007, Ext JS, LLC.
21817  *
21818  * Originally Released Under LGPL - original licence link has changed is not relivant.
21819  *
21820  * Fork - LGPL
21821  * <script type="text/javascript">
21822  */
21823  
21824 /**
21825  * @class Roo.Component
21826  * @extends Roo.util.Observable
21827  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21828  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21829  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21830  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21831  * All visual components (widgets) that require rendering into a layout should subclass Component.
21832  * @constructor
21833  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21834  * 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
21835  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21836  */
21837 Roo.Component = function(config){
21838     config = config || {};
21839     if(config.tagName || config.dom || typeof config == "string"){ // element object
21840         config = {el: config, id: config.id || config};
21841     }
21842     this.initialConfig = config;
21843
21844     Roo.apply(this, config);
21845     this.addEvents({
21846         /**
21847          * @event disable
21848          * Fires after the component is disabled.
21849              * @param {Roo.Component} this
21850              */
21851         disable : true,
21852         /**
21853          * @event enable
21854          * Fires after the component is enabled.
21855              * @param {Roo.Component} this
21856              */
21857         enable : true,
21858         /**
21859          * @event beforeshow
21860          * Fires before the component is shown.  Return false to stop the show.
21861              * @param {Roo.Component} this
21862              */
21863         beforeshow : true,
21864         /**
21865          * @event show
21866          * Fires after the component is shown.
21867              * @param {Roo.Component} this
21868              */
21869         show : true,
21870         /**
21871          * @event beforehide
21872          * Fires before the component is hidden. Return false to stop the hide.
21873              * @param {Roo.Component} this
21874              */
21875         beforehide : true,
21876         /**
21877          * @event hide
21878          * Fires after the component is hidden.
21879              * @param {Roo.Component} this
21880              */
21881         hide : true,
21882         /**
21883          * @event beforerender
21884          * Fires before the component is rendered. Return false to stop the render.
21885              * @param {Roo.Component} this
21886              */
21887         beforerender : true,
21888         /**
21889          * @event render
21890          * Fires after the component is rendered.
21891              * @param {Roo.Component} this
21892              */
21893         render : true,
21894         /**
21895          * @event beforedestroy
21896          * Fires before the component is destroyed. Return false to stop the destroy.
21897              * @param {Roo.Component} this
21898              */
21899         beforedestroy : true,
21900         /**
21901          * @event destroy
21902          * Fires after the component is destroyed.
21903              * @param {Roo.Component} this
21904              */
21905         destroy : true
21906     });
21907     if(!this.id){
21908         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21909     }
21910     Roo.ComponentMgr.register(this);
21911     Roo.Component.superclass.constructor.call(this);
21912     this.initComponent();
21913     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21914         this.render(this.renderTo);
21915         delete this.renderTo;
21916     }
21917 };
21918
21919 /** @private */
21920 Roo.Component.AUTO_ID = 1000;
21921
21922 Roo.extend(Roo.Component, Roo.util.Observable, {
21923     /**
21924      * @scope Roo.Component.prototype
21925      * @type {Boolean}
21926      * true if this component is hidden. Read-only.
21927      */
21928     hidden : false,
21929     /**
21930      * @type {Boolean}
21931      * true if this component is disabled. Read-only.
21932      */
21933     disabled : false,
21934     /**
21935      * @type {Boolean}
21936      * true if this component has been rendered. Read-only.
21937      */
21938     rendered : false,
21939     
21940     /** @cfg {String} disableClass
21941      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21942      */
21943     disabledClass : "x-item-disabled",
21944         /** @cfg {Boolean} allowDomMove
21945          * Whether the component can move the Dom node when rendering (defaults to true).
21946          */
21947     allowDomMove : true,
21948     /** @cfg {String} hideMode
21949      * How this component should hidden. Supported values are
21950      * "visibility" (css visibility), "offsets" (negative offset position) and
21951      * "display" (css display) - defaults to "display".
21952      */
21953     hideMode: 'display',
21954
21955     /** @private */
21956     ctype : "Roo.Component",
21957
21958     /**
21959      * @cfg {String} actionMode 
21960      * which property holds the element that used for  hide() / show() / disable() / enable()
21961      * default is 'el' 
21962      */
21963     actionMode : "el",
21964
21965     /** @private */
21966     getActionEl : function(){
21967         return this[this.actionMode];
21968     },
21969
21970     initComponent : Roo.emptyFn,
21971     /**
21972      * If this is a lazy rendering component, render it to its container element.
21973      * @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.
21974      */
21975     render : function(container, position){
21976         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21977             if(!container && this.el){
21978                 this.el = Roo.get(this.el);
21979                 container = this.el.dom.parentNode;
21980                 this.allowDomMove = false;
21981             }
21982             this.container = Roo.get(container);
21983             this.rendered = true;
21984             if(position !== undefined){
21985                 if(typeof position == 'number'){
21986                     position = this.container.dom.childNodes[position];
21987                 }else{
21988                     position = Roo.getDom(position);
21989                 }
21990             }
21991             this.onRender(this.container, position || null);
21992             if(this.cls){
21993                 this.el.addClass(this.cls);
21994                 delete this.cls;
21995             }
21996             if(this.style){
21997                 this.el.applyStyles(this.style);
21998                 delete this.style;
21999             }
22000             this.fireEvent("render", this);
22001             this.afterRender(this.container);
22002             if(this.hidden){
22003                 this.hide();
22004             }
22005             if(this.disabled){
22006                 this.disable();
22007             }
22008         }
22009         return this;
22010     },
22011
22012     /** @private */
22013     // default function is not really useful
22014     onRender : function(ct, position){
22015         if(this.el){
22016             this.el = Roo.get(this.el);
22017             if(this.allowDomMove !== false){
22018                 ct.dom.insertBefore(this.el.dom, position);
22019             }
22020         }
22021     },
22022
22023     /** @private */
22024     getAutoCreate : function(){
22025         var cfg = typeof this.autoCreate == "object" ?
22026                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22027         if(this.id && !cfg.id){
22028             cfg.id = this.id;
22029         }
22030         return cfg;
22031     },
22032
22033     /** @private */
22034     afterRender : Roo.emptyFn,
22035
22036     /**
22037      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22038      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22039      */
22040     destroy : function(){
22041         if(this.fireEvent("beforedestroy", this) !== false){
22042             this.purgeListeners();
22043             this.beforeDestroy();
22044             if(this.rendered){
22045                 this.el.removeAllListeners();
22046                 this.el.remove();
22047                 if(this.actionMode == "container"){
22048                     this.container.remove();
22049                 }
22050             }
22051             this.onDestroy();
22052             Roo.ComponentMgr.unregister(this);
22053             this.fireEvent("destroy", this);
22054         }
22055     },
22056
22057         /** @private */
22058     beforeDestroy : function(){
22059
22060     },
22061
22062         /** @private */
22063         onDestroy : function(){
22064
22065     },
22066
22067     /**
22068      * Returns the underlying {@link Roo.Element}.
22069      * @return {Roo.Element} The element
22070      */
22071     getEl : function(){
22072         return this.el;
22073     },
22074
22075     /**
22076      * Returns the id of this component.
22077      * @return {String}
22078      */
22079     getId : function(){
22080         return this.id;
22081     },
22082
22083     /**
22084      * Try to focus this component.
22085      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22086      * @return {Roo.Component} this
22087      */
22088     focus : function(selectText){
22089         if(this.rendered){
22090             this.el.focus();
22091             if(selectText === true){
22092                 this.el.dom.select();
22093             }
22094         }
22095         return this;
22096     },
22097
22098     /** @private */
22099     blur : function(){
22100         if(this.rendered){
22101             this.el.blur();
22102         }
22103         return this;
22104     },
22105
22106     /**
22107      * Disable this component.
22108      * @return {Roo.Component} this
22109      */
22110     disable : function(){
22111         if(this.rendered){
22112             this.onDisable();
22113         }
22114         this.disabled = true;
22115         this.fireEvent("disable", this);
22116         return this;
22117     },
22118
22119         // private
22120     onDisable : function(){
22121         this.getActionEl().addClass(this.disabledClass);
22122         this.el.dom.disabled = true;
22123     },
22124
22125     /**
22126      * Enable this component.
22127      * @return {Roo.Component} this
22128      */
22129     enable : function(){
22130         if(this.rendered){
22131             this.onEnable();
22132         }
22133         this.disabled = false;
22134         this.fireEvent("enable", this);
22135         return this;
22136     },
22137
22138         // private
22139     onEnable : function(){
22140         this.getActionEl().removeClass(this.disabledClass);
22141         this.el.dom.disabled = false;
22142     },
22143
22144     /**
22145      * Convenience function for setting disabled/enabled by boolean.
22146      * @param {Boolean} disabled
22147      */
22148     setDisabled : function(disabled){
22149         this[disabled ? "disable" : "enable"]();
22150     },
22151
22152     /**
22153      * Show this component.
22154      * @return {Roo.Component} this
22155      */
22156     show: function(){
22157         if(this.fireEvent("beforeshow", this) !== false){
22158             this.hidden = false;
22159             if(this.rendered){
22160                 this.onShow();
22161             }
22162             this.fireEvent("show", this);
22163         }
22164         return this;
22165     },
22166
22167     // private
22168     onShow : function(){
22169         var ae = this.getActionEl();
22170         if(this.hideMode == 'visibility'){
22171             ae.dom.style.visibility = "visible";
22172         }else if(this.hideMode == 'offsets'){
22173             ae.removeClass('x-hidden');
22174         }else{
22175             ae.dom.style.display = "";
22176         }
22177     },
22178
22179     /**
22180      * Hide this component.
22181      * @return {Roo.Component} this
22182      */
22183     hide: function(){
22184         if(this.fireEvent("beforehide", this) !== false){
22185             this.hidden = true;
22186             if(this.rendered){
22187                 this.onHide();
22188             }
22189             this.fireEvent("hide", this);
22190         }
22191         return this;
22192     },
22193
22194     // private
22195     onHide : function(){
22196         var ae = this.getActionEl();
22197         if(this.hideMode == 'visibility'){
22198             ae.dom.style.visibility = "hidden";
22199         }else if(this.hideMode == 'offsets'){
22200             ae.addClass('x-hidden');
22201         }else{
22202             ae.dom.style.display = "none";
22203         }
22204     },
22205
22206     /**
22207      * Convenience function to hide or show this component by boolean.
22208      * @param {Boolean} visible True to show, false to hide
22209      * @return {Roo.Component} this
22210      */
22211     setVisible: function(visible){
22212         if(visible) {
22213             this.show();
22214         }else{
22215             this.hide();
22216         }
22217         return this;
22218     },
22219
22220     /**
22221      * Returns true if this component is visible.
22222      */
22223     isVisible : function(){
22224         return this.getActionEl().isVisible();
22225     },
22226
22227     cloneConfig : function(overrides){
22228         overrides = overrides || {};
22229         var id = overrides.id || Roo.id();
22230         var cfg = Roo.applyIf(overrides, this.initialConfig);
22231         cfg.id = id; // prevent dup id
22232         return new this.constructor(cfg);
22233     }
22234 });/*
22235  * Based on:
22236  * Ext JS Library 1.1.1
22237  * Copyright(c) 2006-2007, Ext JS, LLC.
22238  *
22239  * Originally Released Under LGPL - original licence link has changed is not relivant.
22240  *
22241  * Fork - LGPL
22242  * <script type="text/javascript">
22243  */
22244  (function(){ 
22245 /**
22246  * @class Roo.Layer
22247  * @extends Roo.Element
22248  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22249  * automatic maintaining of shadow/shim positions.
22250  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22251  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22252  * you can pass a string with a CSS class name. False turns off the shadow.
22253  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22254  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22255  * @cfg {String} cls CSS class to add to the element
22256  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22257  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22258  * @constructor
22259  * @param {Object} config An object with config options.
22260  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22261  */
22262
22263 Roo.Layer = function(config, existingEl){
22264     config = config || {};
22265     var dh = Roo.DomHelper;
22266     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22267     if(existingEl){
22268         this.dom = Roo.getDom(existingEl);
22269     }
22270     if(!this.dom){
22271         var o = config.dh || {tag: "div", cls: "x-layer"};
22272         this.dom = dh.append(pel, o);
22273     }
22274     if(config.cls){
22275         this.addClass(config.cls);
22276     }
22277     this.constrain = config.constrain !== false;
22278     this.visibilityMode = Roo.Element.VISIBILITY;
22279     if(config.id){
22280         this.id = this.dom.id = config.id;
22281     }else{
22282         this.id = Roo.id(this.dom);
22283     }
22284     this.zindex = config.zindex || this.getZIndex();
22285     this.position("absolute", this.zindex);
22286     if(config.shadow){
22287         this.shadowOffset = config.shadowOffset || 4;
22288         this.shadow = new Roo.Shadow({
22289             offset : this.shadowOffset,
22290             mode : config.shadow
22291         });
22292     }else{
22293         this.shadowOffset = 0;
22294     }
22295     this.useShim = config.shim !== false && Roo.useShims;
22296     this.useDisplay = config.useDisplay;
22297     this.hide();
22298 };
22299
22300 var supr = Roo.Element.prototype;
22301
22302 // shims are shared among layer to keep from having 100 iframes
22303 var shims = [];
22304
22305 Roo.extend(Roo.Layer, Roo.Element, {
22306
22307     getZIndex : function(){
22308         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22309     },
22310
22311     getShim : function(){
22312         if(!this.useShim){
22313             return null;
22314         }
22315         if(this.shim){
22316             return this.shim;
22317         }
22318         var shim = shims.shift();
22319         if(!shim){
22320             shim = this.createShim();
22321             shim.enableDisplayMode('block');
22322             shim.dom.style.display = 'none';
22323             shim.dom.style.visibility = 'visible';
22324         }
22325         var pn = this.dom.parentNode;
22326         if(shim.dom.parentNode != pn){
22327             pn.insertBefore(shim.dom, this.dom);
22328         }
22329         shim.setStyle('z-index', this.getZIndex()-2);
22330         this.shim = shim;
22331         return shim;
22332     },
22333
22334     hideShim : function(){
22335         if(this.shim){
22336             this.shim.setDisplayed(false);
22337             shims.push(this.shim);
22338             delete this.shim;
22339         }
22340     },
22341
22342     disableShadow : function(){
22343         if(this.shadow){
22344             this.shadowDisabled = true;
22345             this.shadow.hide();
22346             this.lastShadowOffset = this.shadowOffset;
22347             this.shadowOffset = 0;
22348         }
22349     },
22350
22351     enableShadow : function(show){
22352         if(this.shadow){
22353             this.shadowDisabled = false;
22354             this.shadowOffset = this.lastShadowOffset;
22355             delete this.lastShadowOffset;
22356             if(show){
22357                 this.sync(true);
22358             }
22359         }
22360     },
22361
22362     // private
22363     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22364     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22365     sync : function(doShow){
22366         var sw = this.shadow;
22367         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22368             var sh = this.getShim();
22369
22370             var w = this.getWidth(),
22371                 h = this.getHeight();
22372
22373             var l = this.getLeft(true),
22374                 t = this.getTop(true);
22375
22376             if(sw && !this.shadowDisabled){
22377                 if(doShow && !sw.isVisible()){
22378                     sw.show(this);
22379                 }else{
22380                     sw.realign(l, t, w, h);
22381                 }
22382                 if(sh){
22383                     if(doShow){
22384                        sh.show();
22385                     }
22386                     // fit the shim behind the shadow, so it is shimmed too
22387                     var a = sw.adjusts, s = sh.dom.style;
22388                     s.left = (Math.min(l, l+a.l))+"px";
22389                     s.top = (Math.min(t, t+a.t))+"px";
22390                     s.width = (w+a.w)+"px";
22391                     s.height = (h+a.h)+"px";
22392                 }
22393             }else if(sh){
22394                 if(doShow){
22395                    sh.show();
22396                 }
22397                 sh.setSize(w, h);
22398                 sh.setLeftTop(l, t);
22399             }
22400             
22401         }
22402     },
22403
22404     // private
22405     destroy : function(){
22406         this.hideShim();
22407         if(this.shadow){
22408             this.shadow.hide();
22409         }
22410         this.removeAllListeners();
22411         var pn = this.dom.parentNode;
22412         if(pn){
22413             pn.removeChild(this.dom);
22414         }
22415         Roo.Element.uncache(this.id);
22416     },
22417
22418     remove : function(){
22419         this.destroy();
22420     },
22421
22422     // private
22423     beginUpdate : function(){
22424         this.updating = true;
22425     },
22426
22427     // private
22428     endUpdate : function(){
22429         this.updating = false;
22430         this.sync(true);
22431     },
22432
22433     // private
22434     hideUnders : function(negOffset){
22435         if(this.shadow){
22436             this.shadow.hide();
22437         }
22438         this.hideShim();
22439     },
22440
22441     // private
22442     constrainXY : function(){
22443         if(this.constrain){
22444             var vw = Roo.lib.Dom.getViewWidth(),
22445                 vh = Roo.lib.Dom.getViewHeight();
22446             var s = Roo.get(document).getScroll();
22447
22448             var xy = this.getXY();
22449             var x = xy[0], y = xy[1];   
22450             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22451             // only move it if it needs it
22452             var moved = false;
22453             // first validate right/bottom
22454             if((x + w) > vw+s.left){
22455                 x = vw - w - this.shadowOffset;
22456                 moved = true;
22457             }
22458             if((y + h) > vh+s.top){
22459                 y = vh - h - this.shadowOffset;
22460                 moved = true;
22461             }
22462             // then make sure top/left isn't negative
22463             if(x < s.left){
22464                 x = s.left;
22465                 moved = true;
22466             }
22467             if(y < s.top){
22468                 y = s.top;
22469                 moved = true;
22470             }
22471             if(moved){
22472                 if(this.avoidY){
22473                     var ay = this.avoidY;
22474                     if(y <= ay && (y+h) >= ay){
22475                         y = ay-h-5;   
22476                     }
22477                 }
22478                 xy = [x, y];
22479                 this.storeXY(xy);
22480                 supr.setXY.call(this, xy);
22481                 this.sync();
22482             }
22483         }
22484     },
22485
22486     isVisible : function(){
22487         return this.visible;    
22488     },
22489
22490     // private
22491     showAction : function(){
22492         this.visible = true; // track visibility to prevent getStyle calls
22493         if(this.useDisplay === true){
22494             this.setDisplayed("");
22495         }else if(this.lastXY){
22496             supr.setXY.call(this, this.lastXY);
22497         }else if(this.lastLT){
22498             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22499         }
22500     },
22501
22502     // private
22503     hideAction : function(){
22504         this.visible = false;
22505         if(this.useDisplay === true){
22506             this.setDisplayed(false);
22507         }else{
22508             this.setLeftTop(-10000,-10000);
22509         }
22510     },
22511
22512     // overridden Element method
22513     setVisible : function(v, a, d, c, e){
22514         if(v){
22515             this.showAction();
22516         }
22517         if(a && v){
22518             var cb = function(){
22519                 this.sync(true);
22520                 if(c){
22521                     c();
22522                 }
22523             }.createDelegate(this);
22524             supr.setVisible.call(this, true, true, d, cb, e);
22525         }else{
22526             if(!v){
22527                 this.hideUnders(true);
22528             }
22529             var cb = c;
22530             if(a){
22531                 cb = function(){
22532                     this.hideAction();
22533                     if(c){
22534                         c();
22535                     }
22536                 }.createDelegate(this);
22537             }
22538             supr.setVisible.call(this, v, a, d, cb, e);
22539             if(v){
22540                 this.sync(true);
22541             }else if(!a){
22542                 this.hideAction();
22543             }
22544         }
22545     },
22546
22547     storeXY : function(xy){
22548         delete this.lastLT;
22549         this.lastXY = xy;
22550     },
22551
22552     storeLeftTop : function(left, top){
22553         delete this.lastXY;
22554         this.lastLT = [left, top];
22555     },
22556
22557     // private
22558     beforeFx : function(){
22559         this.beforeAction();
22560         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22561     },
22562
22563     // private
22564     afterFx : function(){
22565         Roo.Layer.superclass.afterFx.apply(this, arguments);
22566         this.sync(this.isVisible());
22567     },
22568
22569     // private
22570     beforeAction : function(){
22571         if(!this.updating && this.shadow){
22572             this.shadow.hide();
22573         }
22574     },
22575
22576     // overridden Element method
22577     setLeft : function(left){
22578         this.storeLeftTop(left, this.getTop(true));
22579         supr.setLeft.apply(this, arguments);
22580         this.sync();
22581     },
22582
22583     setTop : function(top){
22584         this.storeLeftTop(this.getLeft(true), top);
22585         supr.setTop.apply(this, arguments);
22586         this.sync();
22587     },
22588
22589     setLeftTop : function(left, top){
22590         this.storeLeftTop(left, top);
22591         supr.setLeftTop.apply(this, arguments);
22592         this.sync();
22593     },
22594
22595     setXY : function(xy, a, d, c, e){
22596         this.fixDisplay();
22597         this.beforeAction();
22598         this.storeXY(xy);
22599         var cb = this.createCB(c);
22600         supr.setXY.call(this, xy, a, d, cb, e);
22601         if(!a){
22602             cb();
22603         }
22604     },
22605
22606     // private
22607     createCB : function(c){
22608         var el = this;
22609         return function(){
22610             el.constrainXY();
22611             el.sync(true);
22612             if(c){
22613                 c();
22614             }
22615         };
22616     },
22617
22618     // overridden Element method
22619     setX : function(x, a, d, c, e){
22620         this.setXY([x, this.getY()], a, d, c, e);
22621     },
22622
22623     // overridden Element method
22624     setY : function(y, a, d, c, e){
22625         this.setXY([this.getX(), y], a, d, c, e);
22626     },
22627
22628     // overridden Element method
22629     setSize : function(w, h, a, d, c, e){
22630         this.beforeAction();
22631         var cb = this.createCB(c);
22632         supr.setSize.call(this, w, h, a, d, cb, e);
22633         if(!a){
22634             cb();
22635         }
22636     },
22637
22638     // overridden Element method
22639     setWidth : function(w, a, d, c, e){
22640         this.beforeAction();
22641         var cb = this.createCB(c);
22642         supr.setWidth.call(this, w, a, d, cb, e);
22643         if(!a){
22644             cb();
22645         }
22646     },
22647
22648     // overridden Element method
22649     setHeight : function(h, a, d, c, e){
22650         this.beforeAction();
22651         var cb = this.createCB(c);
22652         supr.setHeight.call(this, h, a, d, cb, e);
22653         if(!a){
22654             cb();
22655         }
22656     },
22657
22658     // overridden Element method
22659     setBounds : function(x, y, w, h, a, d, c, e){
22660         this.beforeAction();
22661         var cb = this.createCB(c);
22662         if(!a){
22663             this.storeXY([x, y]);
22664             supr.setXY.call(this, [x, y]);
22665             supr.setSize.call(this, w, h, a, d, cb, e);
22666             cb();
22667         }else{
22668             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22669         }
22670         return this;
22671     },
22672     
22673     /**
22674      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22675      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22676      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22677      * @param {Number} zindex The new z-index to set
22678      * @return {this} The Layer
22679      */
22680     setZIndex : function(zindex){
22681         this.zindex = zindex;
22682         this.setStyle("z-index", zindex + 2);
22683         if(this.shadow){
22684             this.shadow.setZIndex(zindex + 1);
22685         }
22686         if(this.shim){
22687             this.shim.setStyle("z-index", zindex);
22688         }
22689     }
22690 });
22691 })();/*
22692  * Based on:
22693  * Ext JS Library 1.1.1
22694  * Copyright(c) 2006-2007, Ext JS, LLC.
22695  *
22696  * Originally Released Under LGPL - original licence link has changed is not relivant.
22697  *
22698  * Fork - LGPL
22699  * <script type="text/javascript">
22700  */
22701
22702
22703 /**
22704  * @class Roo.Shadow
22705  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22706  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22707  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22708  * @constructor
22709  * Create a new Shadow
22710  * @param {Object} config The config object
22711  */
22712 Roo.Shadow = function(config){
22713     Roo.apply(this, config);
22714     if(typeof this.mode != "string"){
22715         this.mode = this.defaultMode;
22716     }
22717     var o = this.offset, a = {h: 0};
22718     var rad = Math.floor(this.offset/2);
22719     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22720         case "drop":
22721             a.w = 0;
22722             a.l = a.t = o;
22723             a.t -= 1;
22724             if(Roo.isIE){
22725                 a.l -= this.offset + rad;
22726                 a.t -= this.offset + rad;
22727                 a.w -= rad;
22728                 a.h -= rad;
22729                 a.t += 1;
22730             }
22731         break;
22732         case "sides":
22733             a.w = (o*2);
22734             a.l = -o;
22735             a.t = o-1;
22736             if(Roo.isIE){
22737                 a.l -= (this.offset - rad);
22738                 a.t -= this.offset + rad;
22739                 a.l += 1;
22740                 a.w -= (this.offset - rad)*2;
22741                 a.w -= rad + 1;
22742                 a.h -= 1;
22743             }
22744         break;
22745         case "frame":
22746             a.w = a.h = (o*2);
22747             a.l = a.t = -o;
22748             a.t += 1;
22749             a.h -= 2;
22750             if(Roo.isIE){
22751                 a.l -= (this.offset - rad);
22752                 a.t -= (this.offset - rad);
22753                 a.l += 1;
22754                 a.w -= (this.offset + rad + 1);
22755                 a.h -= (this.offset + rad);
22756                 a.h += 1;
22757             }
22758         break;
22759     };
22760
22761     this.adjusts = a;
22762 };
22763
22764 Roo.Shadow.prototype = {
22765     /**
22766      * @cfg {String} mode
22767      * The shadow display mode.  Supports the following options:<br />
22768      * sides: Shadow displays on both sides and bottom only<br />
22769      * frame: Shadow displays equally on all four sides<br />
22770      * drop: Traditional bottom-right drop shadow (default)
22771      */
22772     /**
22773      * @cfg {String} offset
22774      * The number of pixels to offset the shadow from the element (defaults to 4)
22775      */
22776     offset: 4,
22777
22778     // private
22779     defaultMode: "drop",
22780
22781     /**
22782      * Displays the shadow under the target element
22783      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22784      */
22785     show : function(target){
22786         target = Roo.get(target);
22787         if(!this.el){
22788             this.el = Roo.Shadow.Pool.pull();
22789             if(this.el.dom.nextSibling != target.dom){
22790                 this.el.insertBefore(target);
22791             }
22792         }
22793         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22794         if(Roo.isIE){
22795             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22796         }
22797         this.realign(
22798             target.getLeft(true),
22799             target.getTop(true),
22800             target.getWidth(),
22801             target.getHeight()
22802         );
22803         this.el.dom.style.display = "block";
22804     },
22805
22806     /**
22807      * Returns true if the shadow is visible, else false
22808      */
22809     isVisible : function(){
22810         return this.el ? true : false;  
22811     },
22812
22813     /**
22814      * Direct alignment when values are already available. Show must be called at least once before
22815      * calling this method to ensure it is initialized.
22816      * @param {Number} left The target element left position
22817      * @param {Number} top The target element top position
22818      * @param {Number} width The target element width
22819      * @param {Number} height The target element height
22820      */
22821     realign : function(l, t, w, h){
22822         if(!this.el){
22823             return;
22824         }
22825         var a = this.adjusts, d = this.el.dom, s = d.style;
22826         var iea = 0;
22827         s.left = (l+a.l)+"px";
22828         s.top = (t+a.t)+"px";
22829         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22830  
22831         if(s.width != sws || s.height != shs){
22832             s.width = sws;
22833             s.height = shs;
22834             if(!Roo.isIE){
22835                 var cn = d.childNodes;
22836                 var sww = Math.max(0, (sw-12))+"px";
22837                 cn[0].childNodes[1].style.width = sww;
22838                 cn[1].childNodes[1].style.width = sww;
22839                 cn[2].childNodes[1].style.width = sww;
22840                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22841             }
22842         }
22843     },
22844
22845     /**
22846      * Hides this shadow
22847      */
22848     hide : function(){
22849         if(this.el){
22850             this.el.dom.style.display = "none";
22851             Roo.Shadow.Pool.push(this.el);
22852             delete this.el;
22853         }
22854     },
22855
22856     /**
22857      * Adjust the z-index of this shadow
22858      * @param {Number} zindex The new z-index
22859      */
22860     setZIndex : function(z){
22861         this.zIndex = z;
22862         if(this.el){
22863             this.el.setStyle("z-index", z);
22864         }
22865     }
22866 };
22867
22868 // Private utility class that manages the internal Shadow cache
22869 Roo.Shadow.Pool = function(){
22870     var p = [];
22871     var markup = Roo.isIE ?
22872                  '<div class="x-ie-shadow"></div>' :
22873                  '<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>';
22874     return {
22875         pull : function(){
22876             var sh = p.shift();
22877             if(!sh){
22878                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22879                 sh.autoBoxAdjust = false;
22880             }
22881             return sh;
22882         },
22883
22884         push : function(sh){
22885             p.push(sh);
22886         }
22887     };
22888 }();/*
22889  * Based on:
22890  * Ext JS Library 1.1.1
22891  * Copyright(c) 2006-2007, Ext JS, LLC.
22892  *
22893  * Originally Released Under LGPL - original licence link has changed is not relivant.
22894  *
22895  * Fork - LGPL
22896  * <script type="text/javascript">
22897  */
22898
22899 /**
22900  * @class Roo.BoxComponent
22901  * @extends Roo.Component
22902  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22903  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22904  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22905  * layout containers.
22906  * @constructor
22907  * @param {Roo.Element/String/Object} config The configuration options.
22908  */
22909 Roo.BoxComponent = function(config){
22910     Roo.Component.call(this, config);
22911     this.addEvents({
22912         /**
22913          * @event resize
22914          * Fires after the component is resized.
22915              * @param {Roo.Component} this
22916              * @param {Number} adjWidth The box-adjusted width that was set
22917              * @param {Number} adjHeight The box-adjusted height that was set
22918              * @param {Number} rawWidth The width that was originally specified
22919              * @param {Number} rawHeight The height that was originally specified
22920              */
22921         resize : true,
22922         /**
22923          * @event move
22924          * Fires after the component is moved.
22925              * @param {Roo.Component} this
22926              * @param {Number} x The new x position
22927              * @param {Number} y The new y position
22928              */
22929         move : true
22930     });
22931 };
22932
22933 Roo.extend(Roo.BoxComponent, Roo.Component, {
22934     // private, set in afterRender to signify that the component has been rendered
22935     boxReady : false,
22936     // private, used to defer height settings to subclasses
22937     deferHeight: false,
22938     /** @cfg {Number} width
22939      * width (optional) size of component
22940      */
22941      /** @cfg {Number} height
22942      * height (optional) size of component
22943      */
22944      
22945     /**
22946      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22947      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22948      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22949      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22950      * @return {Roo.BoxComponent} this
22951      */
22952     setSize : function(w, h){
22953         // support for standard size objects
22954         if(typeof w == 'object'){
22955             h = w.height;
22956             w = w.width;
22957         }
22958         // not rendered
22959         if(!this.boxReady){
22960             this.width = w;
22961             this.height = h;
22962             return this;
22963         }
22964
22965         // prevent recalcs when not needed
22966         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22967             return this;
22968         }
22969         this.lastSize = {width: w, height: h};
22970
22971         var adj = this.adjustSize(w, h);
22972         var aw = adj.width, ah = adj.height;
22973         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22974             var rz = this.getResizeEl();
22975             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22976                 rz.setSize(aw, ah);
22977             }else if(!this.deferHeight && ah !== undefined){
22978                 rz.setHeight(ah);
22979             }else if(aw !== undefined){
22980                 rz.setWidth(aw);
22981             }
22982             this.onResize(aw, ah, w, h);
22983             this.fireEvent('resize', this, aw, ah, w, h);
22984         }
22985         return this;
22986     },
22987
22988     /**
22989      * Gets the current size of the component's underlying element.
22990      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22991      */
22992     getSize : function(){
22993         return this.el.getSize();
22994     },
22995
22996     /**
22997      * Gets the current XY position of the component's underlying element.
22998      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22999      * @return {Array} The XY position of the element (e.g., [100, 200])
23000      */
23001     getPosition : function(local){
23002         if(local === true){
23003             return [this.el.getLeft(true), this.el.getTop(true)];
23004         }
23005         return this.xy || this.el.getXY();
23006     },
23007
23008     /**
23009      * Gets the current box measurements of the component's underlying element.
23010      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23011      * @returns {Object} box An object in the format {x, y, width, height}
23012      */
23013     getBox : function(local){
23014         var s = this.el.getSize();
23015         if(local){
23016             s.x = this.el.getLeft(true);
23017             s.y = this.el.getTop(true);
23018         }else{
23019             var xy = this.xy || this.el.getXY();
23020             s.x = xy[0];
23021             s.y = xy[1];
23022         }
23023         return s;
23024     },
23025
23026     /**
23027      * Sets the current box measurements of the component's underlying element.
23028      * @param {Object} box An object in the format {x, y, width, height}
23029      * @returns {Roo.BoxComponent} this
23030      */
23031     updateBox : function(box){
23032         this.setSize(box.width, box.height);
23033         this.setPagePosition(box.x, box.y);
23034         return this;
23035     },
23036
23037     // protected
23038     getResizeEl : function(){
23039         return this.resizeEl || this.el;
23040     },
23041
23042     // protected
23043     getPositionEl : function(){
23044         return this.positionEl || this.el;
23045     },
23046
23047     /**
23048      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23049      * This method fires the move event.
23050      * @param {Number} left The new left
23051      * @param {Number} top The new top
23052      * @returns {Roo.BoxComponent} this
23053      */
23054     setPosition : function(x, y){
23055         this.x = x;
23056         this.y = y;
23057         if(!this.boxReady){
23058             return this;
23059         }
23060         var adj = this.adjustPosition(x, y);
23061         var ax = adj.x, ay = adj.y;
23062
23063         var el = this.getPositionEl();
23064         if(ax !== undefined || ay !== undefined){
23065             if(ax !== undefined && ay !== undefined){
23066                 el.setLeftTop(ax, ay);
23067             }else if(ax !== undefined){
23068                 el.setLeft(ax);
23069             }else if(ay !== undefined){
23070                 el.setTop(ay);
23071             }
23072             this.onPosition(ax, ay);
23073             this.fireEvent('move', this, ax, ay);
23074         }
23075         return this;
23076     },
23077
23078     /**
23079      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23080      * This method fires the move event.
23081      * @param {Number} x The new x position
23082      * @param {Number} y The new y position
23083      * @returns {Roo.BoxComponent} this
23084      */
23085     setPagePosition : function(x, y){
23086         this.pageX = x;
23087         this.pageY = y;
23088         if(!this.boxReady){
23089             return;
23090         }
23091         if(x === undefined || y === undefined){ // cannot translate undefined points
23092             return;
23093         }
23094         var p = this.el.translatePoints(x, y);
23095         this.setPosition(p.left, p.top);
23096         return this;
23097     },
23098
23099     // private
23100     onRender : function(ct, position){
23101         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23102         if(this.resizeEl){
23103             this.resizeEl = Roo.get(this.resizeEl);
23104         }
23105         if(this.positionEl){
23106             this.positionEl = Roo.get(this.positionEl);
23107         }
23108     },
23109
23110     // private
23111     afterRender : function(){
23112         Roo.BoxComponent.superclass.afterRender.call(this);
23113         this.boxReady = true;
23114         this.setSize(this.width, this.height);
23115         if(this.x || this.y){
23116             this.setPosition(this.x, this.y);
23117         }
23118         if(this.pageX || this.pageY){
23119             this.setPagePosition(this.pageX, this.pageY);
23120         }
23121     },
23122
23123     /**
23124      * Force the component's size to recalculate based on the underlying element's current height and width.
23125      * @returns {Roo.BoxComponent} this
23126      */
23127     syncSize : function(){
23128         delete this.lastSize;
23129         this.setSize(this.el.getWidth(), this.el.getHeight());
23130         return this;
23131     },
23132
23133     /**
23134      * Called after the component is resized, this method is empty by default but can be implemented by any
23135      * subclass that needs to perform custom logic after a resize occurs.
23136      * @param {Number} adjWidth The box-adjusted width that was set
23137      * @param {Number} adjHeight The box-adjusted height that was set
23138      * @param {Number} rawWidth The width that was originally specified
23139      * @param {Number} rawHeight The height that was originally specified
23140      */
23141     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23142
23143     },
23144
23145     /**
23146      * Called after the component is moved, this method is empty by default but can be implemented by any
23147      * subclass that needs to perform custom logic after a move occurs.
23148      * @param {Number} x The new x position
23149      * @param {Number} y The new y position
23150      */
23151     onPosition : function(x, y){
23152
23153     },
23154
23155     // private
23156     adjustSize : function(w, h){
23157         if(this.autoWidth){
23158             w = 'auto';
23159         }
23160         if(this.autoHeight){
23161             h = 'auto';
23162         }
23163         return {width : w, height: h};
23164     },
23165
23166     // private
23167     adjustPosition : function(x, y){
23168         return {x : x, y: y};
23169     }
23170 });/*
23171  * Based on:
23172  * Ext JS Library 1.1.1
23173  * Copyright(c) 2006-2007, Ext JS, LLC.
23174  *
23175  * Originally Released Under LGPL - original licence link has changed is not relivant.
23176  *
23177  * Fork - LGPL
23178  * <script type="text/javascript">
23179  */
23180
23181
23182 /**
23183  * @class Roo.SplitBar
23184  * @extends Roo.util.Observable
23185  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23186  * <br><br>
23187  * Usage:
23188  * <pre><code>
23189 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23190                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23191 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23192 split.minSize = 100;
23193 split.maxSize = 600;
23194 split.animate = true;
23195 split.on('moved', splitterMoved);
23196 </code></pre>
23197  * @constructor
23198  * Create a new SplitBar
23199  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23200  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23201  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23202  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23203                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23204                         position of the SplitBar).
23205  */
23206 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23207     
23208     /** @private */
23209     this.el = Roo.get(dragElement, true);
23210     this.el.dom.unselectable = "on";
23211     /** @private */
23212     this.resizingEl = Roo.get(resizingElement, true);
23213
23214     /**
23215      * @private
23216      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23217      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23218      * @type Number
23219      */
23220     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23221     
23222     /**
23223      * The minimum size of the resizing element. (Defaults to 0)
23224      * @type Number
23225      */
23226     this.minSize = 0;
23227     
23228     /**
23229      * The maximum size of the resizing element. (Defaults to 2000)
23230      * @type Number
23231      */
23232     this.maxSize = 2000;
23233     
23234     /**
23235      * Whether to animate the transition to the new size
23236      * @type Boolean
23237      */
23238     this.animate = false;
23239     
23240     /**
23241      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23242      * @type Boolean
23243      */
23244     this.useShim = false;
23245     
23246     /** @private */
23247     this.shim = null;
23248     
23249     if(!existingProxy){
23250         /** @private */
23251         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23252     }else{
23253         this.proxy = Roo.get(existingProxy).dom;
23254     }
23255     /** @private */
23256     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23257     
23258     /** @private */
23259     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23260     
23261     /** @private */
23262     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23263     
23264     /** @private */
23265     this.dragSpecs = {};
23266     
23267     /**
23268      * @private The adapter to use to positon and resize elements
23269      */
23270     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23271     this.adapter.init(this);
23272     
23273     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23274         /** @private */
23275         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23276         this.el.addClass("x-splitbar-h");
23277     }else{
23278         /** @private */
23279         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23280         this.el.addClass("x-splitbar-v");
23281     }
23282     
23283     this.addEvents({
23284         /**
23285          * @event resize
23286          * Fires when the splitter is moved (alias for {@link #event-moved})
23287          * @param {Roo.SplitBar} this
23288          * @param {Number} newSize the new width or height
23289          */
23290         "resize" : true,
23291         /**
23292          * @event moved
23293          * Fires when the splitter is moved
23294          * @param {Roo.SplitBar} this
23295          * @param {Number} newSize the new width or height
23296          */
23297         "moved" : true,
23298         /**
23299          * @event beforeresize
23300          * Fires before the splitter is dragged
23301          * @param {Roo.SplitBar} this
23302          */
23303         "beforeresize" : true,
23304
23305         "beforeapply" : true
23306     });
23307
23308     Roo.util.Observable.call(this);
23309 };
23310
23311 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23312     onStartProxyDrag : function(x, y){
23313         this.fireEvent("beforeresize", this);
23314         if(!this.overlay){
23315             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23316             o.unselectable();
23317             o.enableDisplayMode("block");
23318             // all splitbars share the same overlay
23319             Roo.SplitBar.prototype.overlay = o;
23320         }
23321         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23322         this.overlay.show();
23323         Roo.get(this.proxy).setDisplayed("block");
23324         var size = this.adapter.getElementSize(this);
23325         this.activeMinSize = this.getMinimumSize();;
23326         this.activeMaxSize = this.getMaximumSize();;
23327         var c1 = size - this.activeMinSize;
23328         var c2 = Math.max(this.activeMaxSize - size, 0);
23329         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23330             this.dd.resetConstraints();
23331             this.dd.setXConstraint(
23332                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23333                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23334             );
23335             this.dd.setYConstraint(0, 0);
23336         }else{
23337             this.dd.resetConstraints();
23338             this.dd.setXConstraint(0, 0);
23339             this.dd.setYConstraint(
23340                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23341                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23342             );
23343          }
23344         this.dragSpecs.startSize = size;
23345         this.dragSpecs.startPoint = [x, y];
23346         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23347     },
23348     
23349     /** 
23350      * @private Called after the drag operation by the DDProxy
23351      */
23352     onEndProxyDrag : function(e){
23353         Roo.get(this.proxy).setDisplayed(false);
23354         var endPoint = Roo.lib.Event.getXY(e);
23355         if(this.overlay){
23356             this.overlay.hide();
23357         }
23358         var newSize;
23359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23360             newSize = this.dragSpecs.startSize + 
23361                 (this.placement == Roo.SplitBar.LEFT ?
23362                     endPoint[0] - this.dragSpecs.startPoint[0] :
23363                     this.dragSpecs.startPoint[0] - endPoint[0]
23364                 );
23365         }else{
23366             newSize = this.dragSpecs.startSize + 
23367                 (this.placement == Roo.SplitBar.TOP ?
23368                     endPoint[1] - this.dragSpecs.startPoint[1] :
23369                     this.dragSpecs.startPoint[1] - endPoint[1]
23370                 );
23371         }
23372         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23373         if(newSize != this.dragSpecs.startSize){
23374             if(this.fireEvent('beforeapply', this, newSize) !== false){
23375                 this.adapter.setElementSize(this, newSize);
23376                 this.fireEvent("moved", this, newSize);
23377                 this.fireEvent("resize", this, newSize);
23378             }
23379         }
23380     },
23381     
23382     /**
23383      * Get the adapter this SplitBar uses
23384      * @return The adapter object
23385      */
23386     getAdapter : function(){
23387         return this.adapter;
23388     },
23389     
23390     /**
23391      * Set the adapter this SplitBar uses
23392      * @param {Object} adapter A SplitBar adapter object
23393      */
23394     setAdapter : function(adapter){
23395         this.adapter = adapter;
23396         this.adapter.init(this);
23397     },
23398     
23399     /**
23400      * Gets the minimum size for the resizing element
23401      * @return {Number} The minimum size
23402      */
23403     getMinimumSize : function(){
23404         return this.minSize;
23405     },
23406     
23407     /**
23408      * Sets the minimum size for the resizing element
23409      * @param {Number} minSize The minimum size
23410      */
23411     setMinimumSize : function(minSize){
23412         this.minSize = minSize;
23413     },
23414     
23415     /**
23416      * Gets the maximum size for the resizing element
23417      * @return {Number} The maximum size
23418      */
23419     getMaximumSize : function(){
23420         return this.maxSize;
23421     },
23422     
23423     /**
23424      * Sets the maximum size for the resizing element
23425      * @param {Number} maxSize The maximum size
23426      */
23427     setMaximumSize : function(maxSize){
23428         this.maxSize = maxSize;
23429     },
23430     
23431     /**
23432      * Sets the initialize size for the resizing element
23433      * @param {Number} size The initial size
23434      */
23435     setCurrentSize : function(size){
23436         var oldAnimate = this.animate;
23437         this.animate = false;
23438         this.adapter.setElementSize(this, size);
23439         this.animate = oldAnimate;
23440     },
23441     
23442     /**
23443      * Destroy this splitbar. 
23444      * @param {Boolean} removeEl True to remove the element
23445      */
23446     destroy : function(removeEl){
23447         if(this.shim){
23448             this.shim.remove();
23449         }
23450         this.dd.unreg();
23451         this.proxy.parentNode.removeChild(this.proxy);
23452         if(removeEl){
23453             this.el.remove();
23454         }
23455     }
23456 });
23457
23458 /**
23459  * @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.
23460  */
23461 Roo.SplitBar.createProxy = function(dir){
23462     var proxy = new Roo.Element(document.createElement("div"));
23463     proxy.unselectable();
23464     var cls = 'x-splitbar-proxy';
23465     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23466     document.body.appendChild(proxy.dom);
23467     return proxy.dom;
23468 };
23469
23470 /** 
23471  * @class Roo.SplitBar.BasicLayoutAdapter
23472  * Default Adapter. It assumes the splitter and resizing element are not positioned
23473  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23474  */
23475 Roo.SplitBar.BasicLayoutAdapter = function(){
23476 };
23477
23478 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23479     // do nothing for now
23480     init : function(s){
23481     
23482     },
23483     /**
23484      * Called before drag operations to get the current size of the resizing element. 
23485      * @param {Roo.SplitBar} s The SplitBar using this adapter
23486      */
23487      getElementSize : function(s){
23488         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23489             return s.resizingEl.getWidth();
23490         }else{
23491             return s.resizingEl.getHeight();
23492         }
23493     },
23494     
23495     /**
23496      * Called after drag operations to set the size of the resizing element.
23497      * @param {Roo.SplitBar} s The SplitBar using this adapter
23498      * @param {Number} newSize The new size to set
23499      * @param {Function} onComplete A function to be invoked when resizing is complete
23500      */
23501     setElementSize : function(s, newSize, onComplete){
23502         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23503             if(!s.animate){
23504                 s.resizingEl.setWidth(newSize);
23505                 if(onComplete){
23506                     onComplete(s, newSize);
23507                 }
23508             }else{
23509                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23510             }
23511         }else{
23512             
23513             if(!s.animate){
23514                 s.resizingEl.setHeight(newSize);
23515                 if(onComplete){
23516                     onComplete(s, newSize);
23517                 }
23518             }else{
23519                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23520             }
23521         }
23522     }
23523 };
23524
23525 /** 
23526  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23527  * @extends Roo.SplitBar.BasicLayoutAdapter
23528  * Adapter that  moves the splitter element to align with the resized sizing element. 
23529  * Used with an absolute positioned SplitBar.
23530  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23531  * document.body, make sure you assign an id to the body element.
23532  */
23533 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23534     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23535     this.container = Roo.get(container);
23536 };
23537
23538 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23539     init : function(s){
23540         this.basic.init(s);
23541     },
23542     
23543     getElementSize : function(s){
23544         return this.basic.getElementSize(s);
23545     },
23546     
23547     setElementSize : function(s, newSize, onComplete){
23548         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23549     },
23550     
23551     moveSplitter : function(s){
23552         var yes = Roo.SplitBar;
23553         switch(s.placement){
23554             case yes.LEFT:
23555                 s.el.setX(s.resizingEl.getRight());
23556                 break;
23557             case yes.RIGHT:
23558                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23559                 break;
23560             case yes.TOP:
23561                 s.el.setY(s.resizingEl.getBottom());
23562                 break;
23563             case yes.BOTTOM:
23564                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23565                 break;
23566         }
23567     }
23568 };
23569
23570 /**
23571  * Orientation constant - Create a vertical SplitBar
23572  * @static
23573  * @type Number
23574  */
23575 Roo.SplitBar.VERTICAL = 1;
23576
23577 /**
23578  * Orientation constant - Create a horizontal SplitBar
23579  * @static
23580  * @type Number
23581  */
23582 Roo.SplitBar.HORIZONTAL = 2;
23583
23584 /**
23585  * Placement constant - The resizing element is to the left of the splitter element
23586  * @static
23587  * @type Number
23588  */
23589 Roo.SplitBar.LEFT = 1;
23590
23591 /**
23592  * Placement constant - The resizing element is to the right of the splitter element
23593  * @static
23594  * @type Number
23595  */
23596 Roo.SplitBar.RIGHT = 2;
23597
23598 /**
23599  * Placement constant - The resizing element is positioned above the splitter element
23600  * @static
23601  * @type Number
23602  */
23603 Roo.SplitBar.TOP = 3;
23604
23605 /**
23606  * Placement constant - The resizing element is positioned under splitter element
23607  * @static
23608  * @type Number
23609  */
23610 Roo.SplitBar.BOTTOM = 4;
23611 /*
23612  * Based on:
23613  * Ext JS Library 1.1.1
23614  * Copyright(c) 2006-2007, Ext JS, LLC.
23615  *
23616  * Originally Released Under LGPL - original licence link has changed is not relivant.
23617  *
23618  * Fork - LGPL
23619  * <script type="text/javascript">
23620  */
23621
23622 /**
23623  * @class Roo.View
23624  * @extends Roo.util.Observable
23625  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23626  * This class also supports single and multi selection modes. <br>
23627  * Create a data model bound view:
23628  <pre><code>
23629  var store = new Roo.data.Store(...);
23630
23631  var view = new Roo.View({
23632     el : "my-element",
23633     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23634  
23635     singleSelect: true,
23636     selectedClass: "ydataview-selected",
23637     store: store
23638  });
23639
23640  // listen for node click?
23641  view.on("click", function(vw, index, node, e){
23642  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23643  });
23644
23645  // load XML data
23646  dataModel.load("foobar.xml");
23647  </code></pre>
23648  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23649  * <br><br>
23650  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23651  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23652  * 
23653  * Note: old style constructor is still suported (container, template, config)
23654  * 
23655  * @constructor
23656  * Create a new View
23657  * @param {Object} config The config object
23658  * 
23659  */
23660 Roo.View = function(config, depreciated_tpl, depreciated_config){
23661     
23662     if (typeof(depreciated_tpl) == 'undefined') {
23663         // new way.. - universal constructor.
23664         Roo.apply(this, config);
23665         this.el  = Roo.get(this.el);
23666     } else {
23667         // old format..
23668         this.el  = Roo.get(config);
23669         this.tpl = depreciated_tpl;
23670         Roo.apply(this, depreciated_config);
23671     }
23672      
23673     
23674     if(typeof(this.tpl) == "string"){
23675         this.tpl = new Roo.Template(this.tpl);
23676     } else {
23677         // support xtype ctors..
23678         this.tpl = new Roo.factory(this.tpl, Roo);
23679     }
23680     
23681     
23682     this.tpl.compile();
23683    
23684
23685      
23686     /** @private */
23687     this.addEvents({
23688         /**
23689          * @event beforeclick
23690          * Fires before a click is processed. Returns false to cancel the default action.
23691          * @param {Roo.View} this
23692          * @param {Number} index The index of the target node
23693          * @param {HTMLElement} node The target node
23694          * @param {Roo.EventObject} e The raw event object
23695          */
23696             "beforeclick" : true,
23697         /**
23698          * @event click
23699          * Fires when a template node is clicked.
23700          * @param {Roo.View} this
23701          * @param {Number} index The index of the target node
23702          * @param {HTMLElement} node The target node
23703          * @param {Roo.EventObject} e The raw event object
23704          */
23705             "click" : true,
23706         /**
23707          * @event dblclick
23708          * Fires when a template node is double clicked.
23709          * @param {Roo.View} this
23710          * @param {Number} index The index of the target node
23711          * @param {HTMLElement} node The target node
23712          * @param {Roo.EventObject} e The raw event object
23713          */
23714             "dblclick" : true,
23715         /**
23716          * @event contextmenu
23717          * Fires when a template node is right clicked.
23718          * @param {Roo.View} this
23719          * @param {Number} index The index of the target node
23720          * @param {HTMLElement} node The target node
23721          * @param {Roo.EventObject} e The raw event object
23722          */
23723             "contextmenu" : true,
23724         /**
23725          * @event selectionchange
23726          * Fires when the selected nodes change.
23727          * @param {Roo.View} this
23728          * @param {Array} selections Array of the selected nodes
23729          */
23730             "selectionchange" : true,
23731     
23732         /**
23733          * @event beforeselect
23734          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23735          * @param {Roo.View} this
23736          * @param {HTMLElement} node The node to be selected
23737          * @param {Array} selections Array of currently selected nodes
23738          */
23739             "beforeselect" : true,
23740         /**
23741          * @event preparedata
23742          * Fires on every row to render, to allow you to change the data.
23743          * @param {Roo.View} this
23744          * @param {Object} data to be rendered (change this)
23745          */
23746           "preparedata" : true
23747         });
23748
23749     this.el.on({
23750         "click": this.onClick,
23751         "dblclick": this.onDblClick,
23752         "contextmenu": this.onContextMenu,
23753         scope:this
23754     });
23755
23756     this.selections = [];
23757     this.nodes = [];
23758     this.cmp = new Roo.CompositeElementLite([]);
23759     if(this.store){
23760         this.store = Roo.factory(this.store, Roo.data);
23761         this.setStore(this.store, true);
23762     }
23763     Roo.View.superclass.constructor.call(this);
23764 };
23765
23766 Roo.extend(Roo.View, Roo.util.Observable, {
23767     
23768      /**
23769      * @cfg {Roo.data.Store} store Data store to load data from.
23770      */
23771     store : false,
23772     
23773     /**
23774      * @cfg {String|Roo.Element} el The container element.
23775      */
23776     el : '',
23777     
23778     /**
23779      * @cfg {String|Roo.Template} tpl The template used by this View 
23780      */
23781     tpl : false,
23782     /**
23783      * @cfg {String} dataName the named area of the template to use as the data area
23784      *                          Works with domtemplates roo-name="name"
23785      */
23786     dataName: false,
23787     /**
23788      * @cfg {String} selectedClass The css class to add to selected nodes
23789      */
23790     selectedClass : "x-view-selected",
23791      /**
23792      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23793      */
23794     emptyText : "",
23795     /**
23796      * @cfg {Boolean} multiSelect Allow multiple selection
23797      */
23798     multiSelect : false,
23799     /**
23800      * @cfg {Boolean} singleSelect Allow single selection
23801      */
23802     singleSelect:  false,
23803     
23804     /**
23805      * @cfg {Boolean} toggleSelect - selecting 
23806      */
23807     toggleSelect : false,
23808     
23809     /**
23810      * Returns the element this view is bound to.
23811      * @return {Roo.Element}
23812      */
23813     getEl : function(){
23814         return this.el;
23815     },
23816
23817     /**
23818      * Refreshes the view.
23819      */
23820     refresh : function(){
23821         var t = this.tpl;
23822         
23823         // if we are using something like 'domtemplate', then
23824         // the what gets used is:
23825         // t.applySubtemplate(NAME, data, wrapping data..)
23826         // the outer template then get' applied with
23827         //     the store 'extra data'
23828         // and the body get's added to the
23829         //      roo-name="data" node?
23830         //      <span class='roo-tpl-{name}'></span> ?????
23831         
23832         
23833         
23834         this.clearSelections();
23835         this.el.update("");
23836         var html = [];
23837         var records = this.store.getRange();
23838         if(records.length < 1) {
23839             
23840             // is this valid??  = should it render a template??
23841             
23842             this.el.update(this.emptyText);
23843             return;
23844         }
23845         var el = this.el;
23846         if (this.dataName) {
23847             this.el.update(t.apply(this.store.meta)); //????
23848             el = this.el.child('.roo-tpl-' + this.dataName);
23849         }
23850         
23851         for(var i = 0, len = records.length; i < len; i++){
23852             var data = this.prepareData(records[i].data, i, records[i]);
23853             this.fireEvent("preparedata", this, data, i, records[i]);
23854             html[html.length] = Roo.util.Format.trim(
23855                 this.dataName ?
23856                     t.applySubtemplate(this.dataName, data, this.store.meta) :
23857                     t.apply(data)
23858             );
23859         }
23860         
23861         
23862         
23863         el.update(html.join(""));
23864         this.nodes = el.dom.childNodes;
23865         this.updateIndexes(0);
23866     },
23867
23868     /**
23869      * Function to override to reformat the data that is sent to
23870      * the template for each node.
23871      * DEPRICATED - use the preparedata event handler.
23872      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23873      * a JSON object for an UpdateManager bound view).
23874      */
23875     prepareData : function(data, index, record)
23876     {
23877         this.fireEvent("preparedata", this, data, index, record);
23878         return data;
23879     },
23880
23881     onUpdate : function(ds, record){
23882         this.clearSelections();
23883         var index = this.store.indexOf(record);
23884         var n = this.nodes[index];
23885         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
23886         n.parentNode.removeChild(n);
23887         this.updateIndexes(index, index);
23888     },
23889
23890     
23891     
23892 // --------- FIXME     
23893     onAdd : function(ds, records, index)
23894     {
23895         this.clearSelections();
23896         if(this.nodes.length == 0){
23897             this.refresh();
23898             return;
23899         }
23900         var n = this.nodes[index];
23901         for(var i = 0, len = records.length; i < len; i++){
23902             var d = this.prepareData(records[i].data, i, records[i]);
23903             if(n){
23904                 this.tpl.insertBefore(n, d);
23905             }else{
23906                 
23907                 this.tpl.append(this.el, d);
23908             }
23909         }
23910         this.updateIndexes(index);
23911     },
23912
23913     onRemove : function(ds, record, index){
23914         this.clearSelections();
23915         var el = this.dataName  ?
23916             this.el.child('.roo-tpl-' + this.dataName) :
23917             this.el; 
23918         el.dom.removeChild(this.nodes[index]);
23919         this.updateIndexes(index);
23920     },
23921
23922     /**
23923      * Refresh an individual node.
23924      * @param {Number} index
23925      */
23926     refreshNode : function(index){
23927         this.onUpdate(this.store, this.store.getAt(index));
23928     },
23929
23930     updateIndexes : function(startIndex, endIndex){
23931         var ns = this.nodes;
23932         startIndex = startIndex || 0;
23933         endIndex = endIndex || ns.length - 1;
23934         for(var i = startIndex; i <= endIndex; i++){
23935             ns[i].nodeIndex = i;
23936         }
23937     },
23938
23939     /**
23940      * Changes the data store this view uses and refresh the view.
23941      * @param {Store} store
23942      */
23943     setStore : function(store, initial){
23944         if(!initial && this.store){
23945             this.store.un("datachanged", this.refresh);
23946             this.store.un("add", this.onAdd);
23947             this.store.un("remove", this.onRemove);
23948             this.store.un("update", this.onUpdate);
23949             this.store.un("clear", this.refresh);
23950         }
23951         if(store){
23952           
23953             store.on("datachanged", this.refresh, this);
23954             store.on("add", this.onAdd, this);
23955             store.on("remove", this.onRemove, this);
23956             store.on("update", this.onUpdate, this);
23957             store.on("clear", this.refresh, this);
23958         }
23959         
23960         if(store){
23961             this.refresh();
23962         }
23963     },
23964
23965     /**
23966      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23967      * @param {HTMLElement} node
23968      * @return {HTMLElement} The template node
23969      */
23970     findItemFromChild : function(node){
23971         var el = this.dataName  ?
23972             this.el.child('.roo-tpl-' + this.dataName,true) :
23973             this.el.dom; 
23974         
23975         if(!node || node.parentNode == el){
23976                     return node;
23977             }
23978             var p = node.parentNode;
23979             while(p && p != el){
23980             if(p.parentNode == el){
23981                 return p;
23982             }
23983             p = p.parentNode;
23984         }
23985             return null;
23986     },
23987
23988     /** @ignore */
23989     onClick : function(e){
23990         var item = this.findItemFromChild(e.getTarget());
23991         if(item){
23992             var index = this.indexOf(item);
23993             if(this.onItemClick(item, index, e) !== false){
23994                 this.fireEvent("click", this, index, item, e);
23995             }
23996         }else{
23997             this.clearSelections();
23998         }
23999     },
24000
24001     /** @ignore */
24002     onContextMenu : function(e){
24003         var item = this.findItemFromChild(e.getTarget());
24004         if(item){
24005             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24006         }
24007     },
24008
24009     /** @ignore */
24010     onDblClick : function(e){
24011         var item = this.findItemFromChild(e.getTarget());
24012         if(item){
24013             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24014         }
24015     },
24016
24017     onItemClick : function(item, index, e)
24018     {
24019         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24020             return false;
24021         }
24022         if (this.toggleSelect) {
24023             var m = this.isSelected(item) ? 'unselect' : 'select';
24024             Roo.log(m);
24025             var _t = this;
24026             _t[m](item, true, false);
24027             return true;
24028         }
24029         if(this.multiSelect || this.singleSelect){
24030             if(this.multiSelect && e.shiftKey && this.lastSelection){
24031                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24032             }else{
24033                 this.select(item, this.multiSelect && e.ctrlKey);
24034                 this.lastSelection = item;
24035             }
24036             e.preventDefault();
24037         }
24038         return true;
24039     },
24040
24041     /**
24042      * Get the number of selected nodes.
24043      * @return {Number}
24044      */
24045     getSelectionCount : function(){
24046         return this.selections.length;
24047     },
24048
24049     /**
24050      * Get the currently selected nodes.
24051      * @return {Array} An array of HTMLElements
24052      */
24053     getSelectedNodes : function(){
24054         return this.selections;
24055     },
24056
24057     /**
24058      * Get the indexes of the selected nodes.
24059      * @return {Array}
24060      */
24061     getSelectedIndexes : function(){
24062         var indexes = [], s = this.selections;
24063         for(var i = 0, len = s.length; i < len; i++){
24064             indexes.push(s[i].nodeIndex);
24065         }
24066         return indexes;
24067     },
24068
24069     /**
24070      * Clear all selections
24071      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24072      */
24073     clearSelections : function(suppressEvent){
24074         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24075             this.cmp.elements = this.selections;
24076             this.cmp.removeClass(this.selectedClass);
24077             this.selections = [];
24078             if(!suppressEvent){
24079                 this.fireEvent("selectionchange", this, this.selections);
24080             }
24081         }
24082     },
24083
24084     /**
24085      * Returns true if the passed node is selected
24086      * @param {HTMLElement/Number} node The node or node index
24087      * @return {Boolean}
24088      */
24089     isSelected : function(node){
24090         var s = this.selections;
24091         if(s.length < 1){
24092             return false;
24093         }
24094         node = this.getNode(node);
24095         return s.indexOf(node) !== -1;
24096     },
24097
24098     /**
24099      * Selects nodes.
24100      * @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
24101      * @param {Boolean} keepExisting (optional) true to keep existing selections
24102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24103      */
24104     select : function(nodeInfo, keepExisting, suppressEvent){
24105         if(nodeInfo instanceof Array){
24106             if(!keepExisting){
24107                 this.clearSelections(true);
24108             }
24109             for(var i = 0, len = nodeInfo.length; i < len; i++){
24110                 this.select(nodeInfo[i], true, true);
24111             }
24112             return;
24113         } 
24114         var node = this.getNode(nodeInfo);
24115         if(!node || this.isSelected(node)){
24116             return; // already selected.
24117         }
24118         if(!keepExisting){
24119             this.clearSelections(true);
24120         }
24121         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24122             Roo.fly(node).addClass(this.selectedClass);
24123             this.selections.push(node);
24124             if(!suppressEvent){
24125                 this.fireEvent("selectionchange", this, this.selections);
24126             }
24127         }
24128         
24129         
24130     },
24131       /**
24132      * Unselects nodes.
24133      * @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
24134      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24135      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24136      */
24137     unselect : function(nodeInfo, keepExisting, suppressEvent)
24138     {
24139         if(nodeInfo instanceof Array){
24140             Roo.each(this.selections, function(s) {
24141                 this.unselect(s, nodeInfo);
24142             }, this);
24143             return;
24144         }
24145         var node = this.getNode(nodeInfo);
24146         if(!node || !this.isSelected(node)){
24147             Roo.log("not selected");
24148             return; // not selected.
24149         }
24150         // fireevent???
24151         var ns = [];
24152         Roo.each(this.selections, function(s) {
24153             if (s == node ) {
24154                 Roo.fly(node).removeClass(this.selectedClass);
24155
24156                 return;
24157             }
24158             ns.push(s);
24159         },this);
24160         
24161         this.selections= ns;
24162         this.fireEvent("selectionchange", this, this.selections);
24163     },
24164
24165     /**
24166      * Gets a template node.
24167      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24168      * @return {HTMLElement} The node or null if it wasn't found
24169      */
24170     getNode : function(nodeInfo){
24171         if(typeof nodeInfo == "string"){
24172             return document.getElementById(nodeInfo);
24173         }else if(typeof nodeInfo == "number"){
24174             return this.nodes[nodeInfo];
24175         }
24176         return nodeInfo;
24177     },
24178
24179     /**
24180      * Gets a range template nodes.
24181      * @param {Number} startIndex
24182      * @param {Number} endIndex
24183      * @return {Array} An array of nodes
24184      */
24185     getNodes : function(start, end){
24186         var ns = this.nodes;
24187         start = start || 0;
24188         end = typeof end == "undefined" ? ns.length - 1 : end;
24189         var nodes = [];
24190         if(start <= end){
24191             for(var i = start; i <= end; i++){
24192                 nodes.push(ns[i]);
24193             }
24194         } else{
24195             for(var i = start; i >= end; i--){
24196                 nodes.push(ns[i]);
24197             }
24198         }
24199         return nodes;
24200     },
24201
24202     /**
24203      * Finds the index of the passed node
24204      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24205      * @return {Number} The index of the node or -1
24206      */
24207     indexOf : function(node){
24208         node = this.getNode(node);
24209         if(typeof node.nodeIndex == "number"){
24210             return node.nodeIndex;
24211         }
24212         var ns = this.nodes;
24213         for(var i = 0, len = ns.length; i < len; i++){
24214             if(ns[i] == node){
24215                 return i;
24216             }
24217         }
24218         return -1;
24219     }
24220 });
24221 /*
24222  * Based on:
24223  * Ext JS Library 1.1.1
24224  * Copyright(c) 2006-2007, Ext JS, LLC.
24225  *
24226  * Originally Released Under LGPL - original licence link has changed is not relivant.
24227  *
24228  * Fork - LGPL
24229  * <script type="text/javascript">
24230  */
24231
24232 /**
24233  * @class Roo.JsonView
24234  * @extends Roo.View
24235  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24236 <pre><code>
24237 var view = new Roo.JsonView({
24238     container: "my-element",
24239     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24240     multiSelect: true, 
24241     jsonRoot: "data" 
24242 });
24243
24244 // listen for node click?
24245 view.on("click", function(vw, index, node, e){
24246     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24247 });
24248
24249 // direct load of JSON data
24250 view.load("foobar.php");
24251
24252 // Example from my blog list
24253 var tpl = new Roo.Template(
24254     '&lt;div class="entry"&gt;' +
24255     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24256     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24257     "&lt;/div&gt;&lt;hr /&gt;"
24258 );
24259
24260 var moreView = new Roo.JsonView({
24261     container :  "entry-list", 
24262     template : tpl,
24263     jsonRoot: "posts"
24264 });
24265 moreView.on("beforerender", this.sortEntries, this);
24266 moreView.load({
24267     url: "/blog/get-posts.php",
24268     params: "allposts=true",
24269     text: "Loading Blog Entries..."
24270 });
24271 </code></pre>
24272
24273 * Note: old code is supported with arguments : (container, template, config)
24274
24275
24276  * @constructor
24277  * Create a new JsonView
24278  * 
24279  * @param {Object} config The config object
24280  * 
24281  */
24282 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24283     
24284     
24285     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24286
24287     var um = this.el.getUpdateManager();
24288     um.setRenderer(this);
24289     um.on("update", this.onLoad, this);
24290     um.on("failure", this.onLoadException, this);
24291
24292     /**
24293      * @event beforerender
24294      * Fires before rendering of the downloaded JSON data.
24295      * @param {Roo.JsonView} this
24296      * @param {Object} data The JSON data loaded
24297      */
24298     /**
24299      * @event load
24300      * Fires when data is loaded.
24301      * @param {Roo.JsonView} this
24302      * @param {Object} data The JSON data loaded
24303      * @param {Object} response The raw Connect response object
24304      */
24305     /**
24306      * @event loadexception
24307      * Fires when loading fails.
24308      * @param {Roo.JsonView} this
24309      * @param {Object} response The raw Connect response object
24310      */
24311     this.addEvents({
24312         'beforerender' : true,
24313         'load' : true,
24314         'loadexception' : true
24315     });
24316 };
24317 Roo.extend(Roo.JsonView, Roo.View, {
24318     /**
24319      * @type {String} The root property in the loaded JSON object that contains the data
24320      */
24321     jsonRoot : "",
24322
24323     /**
24324      * Refreshes the view.
24325      */
24326     refresh : function(){
24327         this.clearSelections();
24328         this.el.update("");
24329         var html = [];
24330         var o = this.jsonData;
24331         if(o && o.length > 0){
24332             for(var i = 0, len = o.length; i < len; i++){
24333                 var data = this.prepareData(o[i], i, o);
24334                 html[html.length] = this.tpl.apply(data);
24335             }
24336         }else{
24337             html.push(this.emptyText);
24338         }
24339         this.el.update(html.join(""));
24340         this.nodes = this.el.dom.childNodes;
24341         this.updateIndexes(0);
24342     },
24343
24344     /**
24345      * 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.
24346      * @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:
24347      <pre><code>
24348      view.load({
24349          url: "your-url.php",
24350          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24351          callback: yourFunction,
24352          scope: yourObject, //(optional scope)
24353          discardUrl: false,
24354          nocache: false,
24355          text: "Loading...",
24356          timeout: 30,
24357          scripts: false
24358      });
24359      </code></pre>
24360      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24361      * 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.
24362      * @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}
24363      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24364      * @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.
24365      */
24366     load : function(){
24367         var um = this.el.getUpdateManager();
24368         um.update.apply(um, arguments);
24369     },
24370
24371     render : function(el, response){
24372         this.clearSelections();
24373         this.el.update("");
24374         var o;
24375         try{
24376             o = Roo.util.JSON.decode(response.responseText);
24377             if(this.jsonRoot){
24378                 
24379                 o = o[this.jsonRoot];
24380             }
24381         } catch(e){
24382         }
24383         /**
24384          * The current JSON data or null
24385          */
24386         this.jsonData = o;
24387         this.beforeRender();
24388         this.refresh();
24389     },
24390
24391 /**
24392  * Get the number of records in the current JSON dataset
24393  * @return {Number}
24394  */
24395     getCount : function(){
24396         return this.jsonData ? this.jsonData.length : 0;
24397     },
24398
24399 /**
24400  * Returns the JSON object for the specified node(s)
24401  * @param {HTMLElement/Array} node The node or an array of nodes
24402  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24403  * you get the JSON object for the node
24404  */
24405     getNodeData : function(node){
24406         if(node instanceof Array){
24407             var data = [];
24408             for(var i = 0, len = node.length; i < len; i++){
24409                 data.push(this.getNodeData(node[i]));
24410             }
24411             return data;
24412         }
24413         return this.jsonData[this.indexOf(node)] || null;
24414     },
24415
24416     beforeRender : function(){
24417         this.snapshot = this.jsonData;
24418         if(this.sortInfo){
24419             this.sort.apply(this, this.sortInfo);
24420         }
24421         this.fireEvent("beforerender", this, this.jsonData);
24422     },
24423
24424     onLoad : function(el, o){
24425         this.fireEvent("load", this, this.jsonData, o);
24426     },
24427
24428     onLoadException : function(el, o){
24429         this.fireEvent("loadexception", this, o);
24430     },
24431
24432 /**
24433  * Filter the data by a specific property.
24434  * @param {String} property A property on your JSON objects
24435  * @param {String/RegExp} value Either string that the property values
24436  * should start with, or a RegExp to test against the property
24437  */
24438     filter : function(property, value){
24439         if(this.jsonData){
24440             var data = [];
24441             var ss = this.snapshot;
24442             if(typeof value == "string"){
24443                 var vlen = value.length;
24444                 if(vlen == 0){
24445                     this.clearFilter();
24446                     return;
24447                 }
24448                 value = value.toLowerCase();
24449                 for(var i = 0, len = ss.length; i < len; i++){
24450                     var o = ss[i];
24451                     if(o[property].substr(0, vlen).toLowerCase() == value){
24452                         data.push(o);
24453                     }
24454                 }
24455             } else if(value.exec){ // regex?
24456                 for(var i = 0, len = ss.length; i < len; i++){
24457                     var o = ss[i];
24458                     if(value.test(o[property])){
24459                         data.push(o);
24460                     }
24461                 }
24462             } else{
24463                 return;
24464             }
24465             this.jsonData = data;
24466             this.refresh();
24467         }
24468     },
24469
24470 /**
24471  * Filter by a function. The passed function will be called with each
24472  * object in the current dataset. If the function returns true the value is kept,
24473  * otherwise it is filtered.
24474  * @param {Function} fn
24475  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24476  */
24477     filterBy : function(fn, scope){
24478         if(this.jsonData){
24479             var data = [];
24480             var ss = this.snapshot;
24481             for(var i = 0, len = ss.length; i < len; i++){
24482                 var o = ss[i];
24483                 if(fn.call(scope || this, o)){
24484                     data.push(o);
24485                 }
24486             }
24487             this.jsonData = data;
24488             this.refresh();
24489         }
24490     },
24491
24492 /**
24493  * Clears the current filter.
24494  */
24495     clearFilter : function(){
24496         if(this.snapshot && this.jsonData != this.snapshot){
24497             this.jsonData = this.snapshot;
24498             this.refresh();
24499         }
24500     },
24501
24502
24503 /**
24504  * Sorts the data for this view and refreshes it.
24505  * @param {String} property A property on your JSON objects to sort on
24506  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24507  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24508  */
24509     sort : function(property, dir, sortType){
24510         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24511         if(this.jsonData){
24512             var p = property;
24513             var dsc = dir && dir.toLowerCase() == "desc";
24514             var f = function(o1, o2){
24515                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24516                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24517                 ;
24518                 if(v1 < v2){
24519                     return dsc ? +1 : -1;
24520                 } else if(v1 > v2){
24521                     return dsc ? -1 : +1;
24522                 } else{
24523                     return 0;
24524                 }
24525             };
24526             this.jsonData.sort(f);
24527             this.refresh();
24528             if(this.jsonData != this.snapshot){
24529                 this.snapshot.sort(f);
24530             }
24531         }
24532     }
24533 });/*
24534  * Based on:
24535  * Ext JS Library 1.1.1
24536  * Copyright(c) 2006-2007, Ext JS, LLC.
24537  *
24538  * Originally Released Under LGPL - original licence link has changed is not relivant.
24539  *
24540  * Fork - LGPL
24541  * <script type="text/javascript">
24542  */
24543  
24544
24545 /**
24546  * @class Roo.ColorPalette
24547  * @extends Roo.Component
24548  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24549  * Here's an example of typical usage:
24550  * <pre><code>
24551 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24552 cp.render('my-div');
24553
24554 cp.on('select', function(palette, selColor){
24555     // do something with selColor
24556 });
24557 </code></pre>
24558  * @constructor
24559  * Create a new ColorPalette
24560  * @param {Object} config The config object
24561  */
24562 Roo.ColorPalette = function(config){
24563     Roo.ColorPalette.superclass.constructor.call(this, config);
24564     this.addEvents({
24565         /**
24566              * @event select
24567              * Fires when a color is selected
24568              * @param {ColorPalette} this
24569              * @param {String} color The 6-digit color hex code (without the # symbol)
24570              */
24571         select: true
24572     });
24573
24574     if(this.handler){
24575         this.on("select", this.handler, this.scope, true);
24576     }
24577 };
24578 Roo.extend(Roo.ColorPalette, Roo.Component, {
24579     /**
24580      * @cfg {String} itemCls
24581      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24582      */
24583     itemCls : "x-color-palette",
24584     /**
24585      * @cfg {String} value
24586      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24587      * the hex codes are case-sensitive.
24588      */
24589     value : null,
24590     clickEvent:'click',
24591     // private
24592     ctype: "Roo.ColorPalette",
24593
24594     /**
24595      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24596      */
24597     allowReselect : false,
24598
24599     /**
24600      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24601      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24602      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24603      * of colors with the width setting until the box is symmetrical.</p>
24604      * <p>You can override individual colors if needed:</p>
24605      * <pre><code>
24606 var cp = new Roo.ColorPalette();
24607 cp.colors[0] = "FF0000";  // change the first box to red
24608 </code></pre>
24609
24610 Or you can provide a custom array of your own for complete control:
24611 <pre><code>
24612 var cp = new Roo.ColorPalette();
24613 cp.colors = ["000000", "993300", "333300"];
24614 </code></pre>
24615      * @type Array
24616      */
24617     colors : [
24618         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24619         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24620         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24621         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24622         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24623     ],
24624
24625     // private
24626     onRender : function(container, position){
24627         var t = new Roo.MasterTemplate(
24628             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24629         );
24630         var c = this.colors;
24631         for(var i = 0, len = c.length; i < len; i++){
24632             t.add([c[i]]);
24633         }
24634         var el = document.createElement("div");
24635         el.className = this.itemCls;
24636         t.overwrite(el);
24637         container.dom.insertBefore(el, position);
24638         this.el = Roo.get(el);
24639         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24640         if(this.clickEvent != 'click'){
24641             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24642         }
24643     },
24644
24645     // private
24646     afterRender : function(){
24647         Roo.ColorPalette.superclass.afterRender.call(this);
24648         if(this.value){
24649             var s = this.value;
24650             this.value = null;
24651             this.select(s);
24652         }
24653     },
24654
24655     // private
24656     handleClick : function(e, t){
24657         e.preventDefault();
24658         if(!this.disabled){
24659             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24660             this.select(c.toUpperCase());
24661         }
24662     },
24663
24664     /**
24665      * Selects the specified color in the palette (fires the select event)
24666      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24667      */
24668     select : function(color){
24669         color = color.replace("#", "");
24670         if(color != this.value || this.allowReselect){
24671             var el = this.el;
24672             if(this.value){
24673                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24674             }
24675             el.child("a.color-"+color).addClass("x-color-palette-sel");
24676             this.value = color;
24677             this.fireEvent("select", this, color);
24678         }
24679     }
24680 });/*
24681  * Based on:
24682  * Ext JS Library 1.1.1
24683  * Copyright(c) 2006-2007, Ext JS, LLC.
24684  *
24685  * Originally Released Under LGPL - original licence link has changed is not relivant.
24686  *
24687  * Fork - LGPL
24688  * <script type="text/javascript">
24689  */
24690  
24691 /**
24692  * @class Roo.DatePicker
24693  * @extends Roo.Component
24694  * Simple date picker class.
24695  * @constructor
24696  * Create a new DatePicker
24697  * @param {Object} config The config object
24698  */
24699 Roo.DatePicker = function(config){
24700     Roo.DatePicker.superclass.constructor.call(this, config);
24701
24702     this.value = config && config.value ?
24703                  config.value.clearTime() : new Date().clearTime();
24704
24705     this.addEvents({
24706         /**
24707              * @event select
24708              * Fires when a date is selected
24709              * @param {DatePicker} this
24710              * @param {Date} date The selected date
24711              */
24712         'select': true,
24713         /**
24714              * @event monthchange
24715              * Fires when the displayed month changes 
24716              * @param {DatePicker} this
24717              * @param {Date} date The selected month
24718              */
24719         'monthchange': true
24720     });
24721
24722     if(this.handler){
24723         this.on("select", this.handler,  this.scope || this);
24724     }
24725     // build the disabledDatesRE
24726     if(!this.disabledDatesRE && this.disabledDates){
24727         var dd = this.disabledDates;
24728         var re = "(?:";
24729         for(var i = 0; i < dd.length; i++){
24730             re += dd[i];
24731             if(i != dd.length-1) re += "|";
24732         }
24733         this.disabledDatesRE = new RegExp(re + ")");
24734     }
24735 };
24736
24737 Roo.extend(Roo.DatePicker, Roo.Component, {
24738     /**
24739      * @cfg {String} todayText
24740      * The text to display on the button that selects the current date (defaults to "Today")
24741      */
24742     todayText : "Today",
24743     /**
24744      * @cfg {String} okText
24745      * The text to display on the ok button
24746      */
24747     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24748     /**
24749      * @cfg {String} cancelText
24750      * The text to display on the cancel button
24751      */
24752     cancelText : "Cancel",
24753     /**
24754      * @cfg {String} todayTip
24755      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24756      */
24757     todayTip : "{0} (Spacebar)",
24758     /**
24759      * @cfg {Date} minDate
24760      * Minimum allowable date (JavaScript date object, defaults to null)
24761      */
24762     minDate : null,
24763     /**
24764      * @cfg {Date} maxDate
24765      * Maximum allowable date (JavaScript date object, defaults to null)
24766      */
24767     maxDate : null,
24768     /**
24769      * @cfg {String} minText
24770      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24771      */
24772     minText : "This date is before the minimum date",
24773     /**
24774      * @cfg {String} maxText
24775      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24776      */
24777     maxText : "This date is after the maximum date",
24778     /**
24779      * @cfg {String} format
24780      * The default date format string which can be overriden for localization support.  The format must be
24781      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24782      */
24783     format : "m/d/y",
24784     /**
24785      * @cfg {Array} disabledDays
24786      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24787      */
24788     disabledDays : null,
24789     /**
24790      * @cfg {String} disabledDaysText
24791      * The tooltip to display when the date falls on a disabled day (defaults to "")
24792      */
24793     disabledDaysText : "",
24794     /**
24795      * @cfg {RegExp} disabledDatesRE
24796      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24797      */
24798     disabledDatesRE : null,
24799     /**
24800      * @cfg {String} disabledDatesText
24801      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24802      */
24803     disabledDatesText : "",
24804     /**
24805      * @cfg {Boolean} constrainToViewport
24806      * True to constrain the date picker to the viewport (defaults to true)
24807      */
24808     constrainToViewport : true,
24809     /**
24810      * @cfg {Array} monthNames
24811      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24812      */
24813     monthNames : Date.monthNames,
24814     /**
24815      * @cfg {Array} dayNames
24816      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24817      */
24818     dayNames : Date.dayNames,
24819     /**
24820      * @cfg {String} nextText
24821      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24822      */
24823     nextText: 'Next Month (Control+Right)',
24824     /**
24825      * @cfg {String} prevText
24826      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24827      */
24828     prevText: 'Previous Month (Control+Left)',
24829     /**
24830      * @cfg {String} monthYearText
24831      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24832      */
24833     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24834     /**
24835      * @cfg {Number} startDay
24836      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24837      */
24838     startDay : 0,
24839     /**
24840      * @cfg {Bool} showClear
24841      * Show a clear button (usefull for date form elements that can be blank.)
24842      */
24843     
24844     showClear: false,
24845     
24846     /**
24847      * Sets the value of the date field
24848      * @param {Date} value The date to set
24849      */
24850     setValue : function(value){
24851         var old = this.value;
24852         this.value = value.clearTime(true);
24853         if(this.el){
24854             this.update(this.value);
24855         }
24856     },
24857
24858     /**
24859      * Gets the current selected value of the date field
24860      * @return {Date} The selected date
24861      */
24862     getValue : function(){
24863         return this.value;
24864     },
24865
24866     // private
24867     focus : function(){
24868         if(this.el){
24869             this.update(this.activeDate);
24870         }
24871     },
24872
24873     // private
24874     onRender : function(container, position){
24875         var m = [
24876              '<table cellspacing="0">',
24877                 '<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>',
24878                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24879         var dn = this.dayNames;
24880         for(var i = 0; i < 7; i++){
24881             var d = this.startDay+i;
24882             if(d > 6){
24883                 d = d-7;
24884             }
24885             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24886         }
24887         m[m.length] = "</tr></thead><tbody><tr>";
24888         for(var i = 0; i < 42; i++) {
24889             if(i % 7 == 0 && i != 0){
24890                 m[m.length] = "</tr><tr>";
24891             }
24892             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24893         }
24894         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24895             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24896
24897         var el = document.createElement("div");
24898         el.className = "x-date-picker";
24899         el.innerHTML = m.join("");
24900
24901         container.dom.insertBefore(el, position);
24902
24903         this.el = Roo.get(el);
24904         this.eventEl = Roo.get(el.firstChild);
24905
24906         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24907             handler: this.showPrevMonth,
24908             scope: this,
24909             preventDefault:true,
24910             stopDefault:true
24911         });
24912
24913         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24914             handler: this.showNextMonth,
24915             scope: this,
24916             preventDefault:true,
24917             stopDefault:true
24918         });
24919
24920         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24921
24922         this.monthPicker = this.el.down('div.x-date-mp');
24923         this.monthPicker.enableDisplayMode('block');
24924         
24925         var kn = new Roo.KeyNav(this.eventEl, {
24926             "left" : function(e){
24927                 e.ctrlKey ?
24928                     this.showPrevMonth() :
24929                     this.update(this.activeDate.add("d", -1));
24930             },
24931
24932             "right" : function(e){
24933                 e.ctrlKey ?
24934                     this.showNextMonth() :
24935                     this.update(this.activeDate.add("d", 1));
24936             },
24937
24938             "up" : function(e){
24939                 e.ctrlKey ?
24940                     this.showNextYear() :
24941                     this.update(this.activeDate.add("d", -7));
24942             },
24943
24944             "down" : function(e){
24945                 e.ctrlKey ?
24946                     this.showPrevYear() :
24947                     this.update(this.activeDate.add("d", 7));
24948             },
24949
24950             "pageUp" : function(e){
24951                 this.showNextMonth();
24952             },
24953
24954             "pageDown" : function(e){
24955                 this.showPrevMonth();
24956             },
24957
24958             "enter" : function(e){
24959                 e.stopPropagation();
24960                 return true;
24961             },
24962
24963             scope : this
24964         });
24965
24966         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24967
24968         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24969
24970         this.el.unselectable();
24971         
24972         this.cells = this.el.select("table.x-date-inner tbody td");
24973         this.textNodes = this.el.query("table.x-date-inner tbody span");
24974
24975         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24976             text: "&#160;",
24977             tooltip: this.monthYearText
24978         });
24979
24980         this.mbtn.on('click', this.showMonthPicker, this);
24981         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24982
24983
24984         var today = (new Date()).dateFormat(this.format);
24985         
24986         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24987         if (this.showClear) {
24988             baseTb.add( new Roo.Toolbar.Fill());
24989         }
24990         baseTb.add({
24991             text: String.format(this.todayText, today),
24992             tooltip: String.format(this.todayTip, today),
24993             handler: this.selectToday,
24994             scope: this
24995         });
24996         
24997         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24998             
24999         //});
25000         if (this.showClear) {
25001             
25002             baseTb.add( new Roo.Toolbar.Fill());
25003             baseTb.add({
25004                 text: '&#160;',
25005                 cls: 'x-btn-icon x-btn-clear',
25006                 handler: function() {
25007                     //this.value = '';
25008                     this.fireEvent("select", this, '');
25009                 },
25010                 scope: this
25011             });
25012         }
25013         
25014         
25015         if(Roo.isIE){
25016             this.el.repaint();
25017         }
25018         this.update(this.value);
25019     },
25020
25021     createMonthPicker : function(){
25022         if(!this.monthPicker.dom.firstChild){
25023             var buf = ['<table border="0" cellspacing="0">'];
25024             for(var i = 0; i < 6; i++){
25025                 buf.push(
25026                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25027                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25028                     i == 0 ?
25029                     '<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>' :
25030                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25031                 );
25032             }
25033             buf.push(
25034                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25035                     this.okText,
25036                     '</button><button type="button" class="x-date-mp-cancel">',
25037                     this.cancelText,
25038                     '</button></td></tr>',
25039                 '</table>'
25040             );
25041             this.monthPicker.update(buf.join(''));
25042             this.monthPicker.on('click', this.onMonthClick, this);
25043             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25044
25045             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25046             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25047
25048             this.mpMonths.each(function(m, a, i){
25049                 i += 1;
25050                 if((i%2) == 0){
25051                     m.dom.xmonth = 5 + Math.round(i * .5);
25052                 }else{
25053                     m.dom.xmonth = Math.round((i-1) * .5);
25054                 }
25055             });
25056         }
25057     },
25058
25059     showMonthPicker : function(){
25060         this.createMonthPicker();
25061         var size = this.el.getSize();
25062         this.monthPicker.setSize(size);
25063         this.monthPicker.child('table').setSize(size);
25064
25065         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25066         this.updateMPMonth(this.mpSelMonth);
25067         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25068         this.updateMPYear(this.mpSelYear);
25069
25070         this.monthPicker.slideIn('t', {duration:.2});
25071     },
25072
25073     updateMPYear : function(y){
25074         this.mpyear = y;
25075         var ys = this.mpYears.elements;
25076         for(var i = 1; i <= 10; i++){
25077             var td = ys[i-1], y2;
25078             if((i%2) == 0){
25079                 y2 = y + Math.round(i * .5);
25080                 td.firstChild.innerHTML = y2;
25081                 td.xyear = y2;
25082             }else{
25083                 y2 = y - (5-Math.round(i * .5));
25084                 td.firstChild.innerHTML = y2;
25085                 td.xyear = y2;
25086             }
25087             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25088         }
25089     },
25090
25091     updateMPMonth : function(sm){
25092         this.mpMonths.each(function(m, a, i){
25093             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25094         });
25095     },
25096
25097     selectMPMonth: function(m){
25098         
25099     },
25100
25101     onMonthClick : function(e, t){
25102         e.stopEvent();
25103         var el = new Roo.Element(t), pn;
25104         if(el.is('button.x-date-mp-cancel')){
25105             this.hideMonthPicker();
25106         }
25107         else if(el.is('button.x-date-mp-ok')){
25108             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25109             this.hideMonthPicker();
25110         }
25111         else if(pn = el.up('td.x-date-mp-month', 2)){
25112             this.mpMonths.removeClass('x-date-mp-sel');
25113             pn.addClass('x-date-mp-sel');
25114             this.mpSelMonth = pn.dom.xmonth;
25115         }
25116         else if(pn = el.up('td.x-date-mp-year', 2)){
25117             this.mpYears.removeClass('x-date-mp-sel');
25118             pn.addClass('x-date-mp-sel');
25119             this.mpSelYear = pn.dom.xyear;
25120         }
25121         else if(el.is('a.x-date-mp-prev')){
25122             this.updateMPYear(this.mpyear-10);
25123         }
25124         else if(el.is('a.x-date-mp-next')){
25125             this.updateMPYear(this.mpyear+10);
25126         }
25127     },
25128
25129     onMonthDblClick : function(e, t){
25130         e.stopEvent();
25131         var el = new Roo.Element(t), pn;
25132         if(pn = el.up('td.x-date-mp-month', 2)){
25133             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25134             this.hideMonthPicker();
25135         }
25136         else if(pn = el.up('td.x-date-mp-year', 2)){
25137             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25138             this.hideMonthPicker();
25139         }
25140     },
25141
25142     hideMonthPicker : function(disableAnim){
25143         if(this.monthPicker){
25144             if(disableAnim === true){
25145                 this.monthPicker.hide();
25146             }else{
25147                 this.monthPicker.slideOut('t', {duration:.2});
25148             }
25149         }
25150     },
25151
25152     // private
25153     showPrevMonth : function(e){
25154         this.update(this.activeDate.add("mo", -1));
25155     },
25156
25157     // private
25158     showNextMonth : function(e){
25159         this.update(this.activeDate.add("mo", 1));
25160     },
25161
25162     // private
25163     showPrevYear : function(){
25164         this.update(this.activeDate.add("y", -1));
25165     },
25166
25167     // private
25168     showNextYear : function(){
25169         this.update(this.activeDate.add("y", 1));
25170     },
25171
25172     // private
25173     handleMouseWheel : function(e){
25174         var delta = e.getWheelDelta();
25175         if(delta > 0){
25176             this.showPrevMonth();
25177             e.stopEvent();
25178         } else if(delta < 0){
25179             this.showNextMonth();
25180             e.stopEvent();
25181         }
25182     },
25183
25184     // private
25185     handleDateClick : function(e, t){
25186         e.stopEvent();
25187         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25188             this.setValue(new Date(t.dateValue));
25189             this.fireEvent("select", this, this.value);
25190         }
25191     },
25192
25193     // private
25194     selectToday : function(){
25195         this.setValue(new Date().clearTime());
25196         this.fireEvent("select", this, this.value);
25197     },
25198
25199     // private
25200     update : function(date)
25201     {
25202         var vd = this.activeDate;
25203         this.activeDate = date;
25204         if(vd && this.el){
25205             var t = date.getTime();
25206             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25207                 this.cells.removeClass("x-date-selected");
25208                 this.cells.each(function(c){
25209                    if(c.dom.firstChild.dateValue == t){
25210                        c.addClass("x-date-selected");
25211                        setTimeout(function(){
25212                             try{c.dom.firstChild.focus();}catch(e){}
25213                        }, 50);
25214                        return false;
25215                    }
25216                 });
25217                 return;
25218             }
25219         }
25220         
25221         var days = date.getDaysInMonth();
25222         var firstOfMonth = date.getFirstDateOfMonth();
25223         var startingPos = firstOfMonth.getDay()-this.startDay;
25224
25225         if(startingPos <= this.startDay){
25226             startingPos += 7;
25227         }
25228
25229         var pm = date.add("mo", -1);
25230         var prevStart = pm.getDaysInMonth()-startingPos;
25231
25232         var cells = this.cells.elements;
25233         var textEls = this.textNodes;
25234         days += startingPos;
25235
25236         // convert everything to numbers so it's fast
25237         var day = 86400000;
25238         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25239         var today = new Date().clearTime().getTime();
25240         var sel = date.clearTime().getTime();
25241         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25242         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25243         var ddMatch = this.disabledDatesRE;
25244         var ddText = this.disabledDatesText;
25245         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25246         var ddaysText = this.disabledDaysText;
25247         var format = this.format;
25248
25249         var setCellClass = function(cal, cell){
25250             cell.title = "";
25251             var t = d.getTime();
25252             cell.firstChild.dateValue = t;
25253             if(t == today){
25254                 cell.className += " x-date-today";
25255                 cell.title = cal.todayText;
25256             }
25257             if(t == sel){
25258                 cell.className += " x-date-selected";
25259                 setTimeout(function(){
25260                     try{cell.firstChild.focus();}catch(e){}
25261                 }, 50);
25262             }
25263             // disabling
25264             if(t < min) {
25265                 cell.className = " x-date-disabled";
25266                 cell.title = cal.minText;
25267                 return;
25268             }
25269             if(t > max) {
25270                 cell.className = " x-date-disabled";
25271                 cell.title = cal.maxText;
25272                 return;
25273             }
25274             if(ddays){
25275                 if(ddays.indexOf(d.getDay()) != -1){
25276                     cell.title = ddaysText;
25277                     cell.className = " x-date-disabled";
25278                 }
25279             }
25280             if(ddMatch && format){
25281                 var fvalue = d.dateFormat(format);
25282                 if(ddMatch.test(fvalue)){
25283                     cell.title = ddText.replace("%0", fvalue);
25284                     cell.className = " x-date-disabled";
25285                 }
25286             }
25287         };
25288
25289         var i = 0;
25290         for(; i < startingPos; i++) {
25291             textEls[i].innerHTML = (++prevStart);
25292             d.setDate(d.getDate()+1);
25293             cells[i].className = "x-date-prevday";
25294             setCellClass(this, cells[i]);
25295         }
25296         for(; i < days; i++){
25297             intDay = i - startingPos + 1;
25298             textEls[i].innerHTML = (intDay);
25299             d.setDate(d.getDate()+1);
25300             cells[i].className = "x-date-active";
25301             setCellClass(this, cells[i]);
25302         }
25303         var extraDays = 0;
25304         for(; i < 42; i++) {
25305              textEls[i].innerHTML = (++extraDays);
25306              d.setDate(d.getDate()+1);
25307              cells[i].className = "x-date-nextday";
25308              setCellClass(this, cells[i]);
25309         }
25310
25311         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25312         this.fireEvent('monthchange', this, date);
25313         
25314         if(!this.internalRender){
25315             var main = this.el.dom.firstChild;
25316             var w = main.offsetWidth;
25317             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25318             Roo.fly(main).setWidth(w);
25319             this.internalRender = true;
25320             // opera does not respect the auto grow header center column
25321             // then, after it gets a width opera refuses to recalculate
25322             // without a second pass
25323             if(Roo.isOpera && !this.secondPass){
25324                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25325                 this.secondPass = true;
25326                 this.update.defer(10, this, [date]);
25327             }
25328         }
25329         
25330         
25331     }
25332 });        /*
25333  * Based on:
25334  * Ext JS Library 1.1.1
25335  * Copyright(c) 2006-2007, Ext JS, LLC.
25336  *
25337  * Originally Released Under LGPL - original licence link has changed is not relivant.
25338  *
25339  * Fork - LGPL
25340  * <script type="text/javascript">
25341  */
25342 /**
25343  * @class Roo.TabPanel
25344  * @extends Roo.util.Observable
25345  * A lightweight tab container.
25346  * <br><br>
25347  * Usage:
25348  * <pre><code>
25349 // basic tabs 1, built from existing content
25350 var tabs = new Roo.TabPanel("tabs1");
25351 tabs.addTab("script", "View Script");
25352 tabs.addTab("markup", "View Markup");
25353 tabs.activate("script");
25354
25355 // more advanced tabs, built from javascript
25356 var jtabs = new Roo.TabPanel("jtabs");
25357 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25358
25359 // set up the UpdateManager
25360 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25361 var updater = tab2.getUpdateManager();
25362 updater.setDefaultUrl("ajax1.htm");
25363 tab2.on('activate', updater.refresh, updater, true);
25364
25365 // Use setUrl for Ajax loading
25366 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25367 tab3.setUrl("ajax2.htm", null, true);
25368
25369 // Disabled tab
25370 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25371 tab4.disable();
25372
25373 jtabs.activate("jtabs-1");
25374  * </code></pre>
25375  * @constructor
25376  * Create a new TabPanel.
25377  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25378  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25379  */
25380 Roo.TabPanel = function(container, config){
25381     /**
25382     * The container element for this TabPanel.
25383     * @type Roo.Element
25384     */
25385     this.el = Roo.get(container, true);
25386     if(config){
25387         if(typeof config == "boolean"){
25388             this.tabPosition = config ? "bottom" : "top";
25389         }else{
25390             Roo.apply(this, config);
25391         }
25392     }
25393     if(this.tabPosition == "bottom"){
25394         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25395         this.el.addClass("x-tabs-bottom");
25396     }
25397     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25398     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25399     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25400     if(Roo.isIE){
25401         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25402     }
25403     if(this.tabPosition != "bottom"){
25404         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25405          * @type Roo.Element
25406          */
25407         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25408         this.el.addClass("x-tabs-top");
25409     }
25410     this.items = [];
25411
25412     this.bodyEl.setStyle("position", "relative");
25413
25414     this.active = null;
25415     this.activateDelegate = this.activate.createDelegate(this);
25416
25417     this.addEvents({
25418         /**
25419          * @event tabchange
25420          * Fires when the active tab changes
25421          * @param {Roo.TabPanel} this
25422          * @param {Roo.TabPanelItem} activePanel The new active tab
25423          */
25424         "tabchange": true,
25425         /**
25426          * @event beforetabchange
25427          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25428          * @param {Roo.TabPanel} this
25429          * @param {Object} e Set cancel to true on this object to cancel the tab change
25430          * @param {Roo.TabPanelItem} tab The tab being changed to
25431          */
25432         "beforetabchange" : true
25433     });
25434
25435     Roo.EventManager.onWindowResize(this.onResize, this);
25436     this.cpad = this.el.getPadding("lr");
25437     this.hiddenCount = 0;
25438
25439
25440     // toolbar on the tabbar support...
25441     if (this.toolbar) {
25442         var tcfg = this.toolbar;
25443         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25444         this.toolbar = new Roo.Toolbar(tcfg);
25445         if (Roo.isSafari) {
25446             var tbl = tcfg.container.child('table', true);
25447             tbl.setAttribute('width', '100%');
25448         }
25449         
25450     }
25451    
25452
25453
25454     Roo.TabPanel.superclass.constructor.call(this);
25455 };
25456
25457 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25458     /*
25459      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25460      */
25461     tabPosition : "top",
25462     /*
25463      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25464      */
25465     currentTabWidth : 0,
25466     /*
25467      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25468      */
25469     minTabWidth : 40,
25470     /*
25471      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25472      */
25473     maxTabWidth : 250,
25474     /*
25475      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25476      */
25477     preferredTabWidth : 175,
25478     /*
25479      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25480      */
25481     resizeTabs : false,
25482     /*
25483      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25484      */
25485     monitorResize : true,
25486     /*
25487      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25488      */
25489     toolbar : false,
25490
25491     /**
25492      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25493      * @param {String} id The id of the div to use <b>or create</b>
25494      * @param {String} text The text for the tab
25495      * @param {String} content (optional) Content to put in the TabPanelItem body
25496      * @param {Boolean} closable (optional) True to create a close icon on the tab
25497      * @return {Roo.TabPanelItem} The created TabPanelItem
25498      */
25499     addTab : function(id, text, content, closable){
25500         var item = new Roo.TabPanelItem(this, id, text, closable);
25501         this.addTabItem(item);
25502         if(content){
25503             item.setContent(content);
25504         }
25505         return item;
25506     },
25507
25508     /**
25509      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25510      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25511      * @return {Roo.TabPanelItem}
25512      */
25513     getTab : function(id){
25514         return this.items[id];
25515     },
25516
25517     /**
25518      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25519      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25520      */
25521     hideTab : function(id){
25522         var t = this.items[id];
25523         if(!t.isHidden()){
25524            t.setHidden(true);
25525            this.hiddenCount++;
25526            this.autoSizeTabs();
25527         }
25528     },
25529
25530     /**
25531      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25532      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25533      */
25534     unhideTab : function(id){
25535         var t = this.items[id];
25536         if(t.isHidden()){
25537            t.setHidden(false);
25538            this.hiddenCount--;
25539            this.autoSizeTabs();
25540         }
25541     },
25542
25543     /**
25544      * Adds an existing {@link Roo.TabPanelItem}.
25545      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25546      */
25547     addTabItem : function(item){
25548         this.items[item.id] = item;
25549         this.items.push(item);
25550         if(this.resizeTabs){
25551            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25552            this.autoSizeTabs();
25553         }else{
25554             item.autoSize();
25555         }
25556     },
25557
25558     /**
25559      * Removes a {@link Roo.TabPanelItem}.
25560      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25561      */
25562     removeTab : function(id){
25563         var items = this.items;
25564         var tab = items[id];
25565         if(!tab) { return; }
25566         var index = items.indexOf(tab);
25567         if(this.active == tab && items.length > 1){
25568             var newTab = this.getNextAvailable(index);
25569             if(newTab) {
25570                 newTab.activate();
25571             }
25572         }
25573         this.stripEl.dom.removeChild(tab.pnode.dom);
25574         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25575             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25576         }
25577         items.splice(index, 1);
25578         delete this.items[tab.id];
25579         tab.fireEvent("close", tab);
25580         tab.purgeListeners();
25581         this.autoSizeTabs();
25582     },
25583
25584     getNextAvailable : function(start){
25585         var items = this.items;
25586         var index = start;
25587         // look for a next tab that will slide over to
25588         // replace the one being removed
25589         while(index < items.length){
25590             var item = items[++index];
25591             if(item && !item.isHidden()){
25592                 return item;
25593             }
25594         }
25595         // if one isn't found select the previous tab (on the left)
25596         index = start;
25597         while(index >= 0){
25598             var item = items[--index];
25599             if(item && !item.isHidden()){
25600                 return item;
25601             }
25602         }
25603         return null;
25604     },
25605
25606     /**
25607      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25608      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25609      */
25610     disableTab : function(id){
25611         var tab = this.items[id];
25612         if(tab && this.active != tab){
25613             tab.disable();
25614         }
25615     },
25616
25617     /**
25618      * Enables a {@link Roo.TabPanelItem} that is disabled.
25619      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25620      */
25621     enableTab : function(id){
25622         var tab = this.items[id];
25623         tab.enable();
25624     },
25625
25626     /**
25627      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25628      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25629      * @return {Roo.TabPanelItem} The TabPanelItem.
25630      */
25631     activate : function(id){
25632         var tab = this.items[id];
25633         if(!tab){
25634             return null;
25635         }
25636         if(tab == this.active || tab.disabled){
25637             return tab;
25638         }
25639         var e = {};
25640         this.fireEvent("beforetabchange", this, e, tab);
25641         if(e.cancel !== true && !tab.disabled){
25642             if(this.active){
25643                 this.active.hide();
25644             }
25645             this.active = this.items[id];
25646             this.active.show();
25647             this.fireEvent("tabchange", this, this.active);
25648         }
25649         return tab;
25650     },
25651
25652     /**
25653      * Gets the active {@link Roo.TabPanelItem}.
25654      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25655      */
25656     getActiveTab : function(){
25657         return this.active;
25658     },
25659
25660     /**
25661      * Updates the tab body element to fit the height of the container element
25662      * for overflow scrolling
25663      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25664      */
25665     syncHeight : function(targetHeight){
25666         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25667         var bm = this.bodyEl.getMargins();
25668         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25669         this.bodyEl.setHeight(newHeight);
25670         return newHeight;
25671     },
25672
25673     onResize : function(){
25674         if(this.monitorResize){
25675             this.autoSizeTabs();
25676         }
25677     },
25678
25679     /**
25680      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25681      */
25682     beginUpdate : function(){
25683         this.updating = true;
25684     },
25685
25686     /**
25687      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25688      */
25689     endUpdate : function(){
25690         this.updating = false;
25691         this.autoSizeTabs();
25692     },
25693
25694     /**
25695      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25696      */
25697     autoSizeTabs : function(){
25698         var count = this.items.length;
25699         var vcount = count - this.hiddenCount;
25700         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25701         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25702         var availWidth = Math.floor(w / vcount);
25703         var b = this.stripBody;
25704         if(b.getWidth() > w){
25705             var tabs = this.items;
25706             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25707             if(availWidth < this.minTabWidth){
25708                 /*if(!this.sleft){    // incomplete scrolling code
25709                     this.createScrollButtons();
25710                 }
25711                 this.showScroll();
25712                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25713             }
25714         }else{
25715             if(this.currentTabWidth < this.preferredTabWidth){
25716                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25717             }
25718         }
25719     },
25720
25721     /**
25722      * Returns the number of tabs in this TabPanel.
25723      * @return {Number}
25724      */
25725      getCount : function(){
25726          return this.items.length;
25727      },
25728
25729     /**
25730      * Resizes all the tabs to the passed width
25731      * @param {Number} The new width
25732      */
25733     setTabWidth : function(width){
25734         this.currentTabWidth = width;
25735         for(var i = 0, len = this.items.length; i < len; i++) {
25736                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25737         }
25738     },
25739
25740     /**
25741      * Destroys this TabPanel
25742      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25743      */
25744     destroy : function(removeEl){
25745         Roo.EventManager.removeResizeListener(this.onResize, this);
25746         for(var i = 0, len = this.items.length; i < len; i++){
25747             this.items[i].purgeListeners();
25748         }
25749         if(removeEl === true){
25750             this.el.update("");
25751             this.el.remove();
25752         }
25753     }
25754 });
25755
25756 /**
25757  * @class Roo.TabPanelItem
25758  * @extends Roo.util.Observable
25759  * Represents an individual item (tab plus body) in a TabPanel.
25760  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25761  * @param {String} id The id of this TabPanelItem
25762  * @param {String} text The text for the tab of this TabPanelItem
25763  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25764  */
25765 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25766     /**
25767      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25768      * @type Roo.TabPanel
25769      */
25770     this.tabPanel = tabPanel;
25771     /**
25772      * The id for this TabPanelItem
25773      * @type String
25774      */
25775     this.id = id;
25776     /** @private */
25777     this.disabled = false;
25778     /** @private */
25779     this.text = text;
25780     /** @private */
25781     this.loaded = false;
25782     this.closable = closable;
25783
25784     /**
25785      * The body element for this TabPanelItem.
25786      * @type Roo.Element
25787      */
25788     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25789     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25790     this.bodyEl.setStyle("display", "block");
25791     this.bodyEl.setStyle("zoom", "1");
25792     this.hideAction();
25793
25794     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25795     /** @private */
25796     this.el = Roo.get(els.el, true);
25797     this.inner = Roo.get(els.inner, true);
25798     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25799     this.pnode = Roo.get(els.el.parentNode, true);
25800     this.el.on("mousedown", this.onTabMouseDown, this);
25801     this.el.on("click", this.onTabClick, this);
25802     /** @private */
25803     if(closable){
25804         var c = Roo.get(els.close, true);
25805         c.dom.title = this.closeText;
25806         c.addClassOnOver("close-over");
25807         c.on("click", this.closeClick, this);
25808      }
25809
25810     this.addEvents({
25811          /**
25812          * @event activate
25813          * Fires when this tab becomes the active tab.
25814          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25815          * @param {Roo.TabPanelItem} this
25816          */
25817         "activate": true,
25818         /**
25819          * @event beforeclose
25820          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25821          * @param {Roo.TabPanelItem} this
25822          * @param {Object} e Set cancel to true on this object to cancel the close.
25823          */
25824         "beforeclose": true,
25825         /**
25826          * @event close
25827          * Fires when this tab is closed.
25828          * @param {Roo.TabPanelItem} this
25829          */
25830          "close": true,
25831         /**
25832          * @event deactivate
25833          * Fires when this tab is no longer the active tab.
25834          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25835          * @param {Roo.TabPanelItem} this
25836          */
25837          "deactivate" : true
25838     });
25839     this.hidden = false;
25840
25841     Roo.TabPanelItem.superclass.constructor.call(this);
25842 };
25843
25844 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25845     purgeListeners : function(){
25846        Roo.util.Observable.prototype.purgeListeners.call(this);
25847        this.el.removeAllListeners();
25848     },
25849     /**
25850      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25851      */
25852     show : function(){
25853         this.pnode.addClass("on");
25854         this.showAction();
25855         if(Roo.isOpera){
25856             this.tabPanel.stripWrap.repaint();
25857         }
25858         this.fireEvent("activate", this.tabPanel, this);
25859     },
25860
25861     /**
25862      * Returns true if this tab is the active tab.
25863      * @return {Boolean}
25864      */
25865     isActive : function(){
25866         return this.tabPanel.getActiveTab() == this;
25867     },
25868
25869     /**
25870      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25871      */
25872     hide : function(){
25873         this.pnode.removeClass("on");
25874         this.hideAction();
25875         this.fireEvent("deactivate", this.tabPanel, this);
25876     },
25877
25878     hideAction : function(){
25879         this.bodyEl.hide();
25880         this.bodyEl.setStyle("position", "absolute");
25881         this.bodyEl.setLeft("-20000px");
25882         this.bodyEl.setTop("-20000px");
25883     },
25884
25885     showAction : function(){
25886         this.bodyEl.setStyle("position", "relative");
25887         this.bodyEl.setTop("");
25888         this.bodyEl.setLeft("");
25889         this.bodyEl.show();
25890     },
25891
25892     /**
25893      * Set the tooltip for the tab.
25894      * @param {String} tooltip The tab's tooltip
25895      */
25896     setTooltip : function(text){
25897         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25898             this.textEl.dom.qtip = text;
25899             this.textEl.dom.removeAttribute('title');
25900         }else{
25901             this.textEl.dom.title = text;
25902         }
25903     },
25904
25905     onTabClick : function(e){
25906         e.preventDefault();
25907         this.tabPanel.activate(this.id);
25908     },
25909
25910     onTabMouseDown : function(e){
25911         e.preventDefault();
25912         this.tabPanel.activate(this.id);
25913     },
25914
25915     getWidth : function(){
25916         return this.inner.getWidth();
25917     },
25918
25919     setWidth : function(width){
25920         var iwidth = width - this.pnode.getPadding("lr");
25921         this.inner.setWidth(iwidth);
25922         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25923         this.pnode.setWidth(width);
25924     },
25925
25926     /**
25927      * Show or hide the tab
25928      * @param {Boolean} hidden True to hide or false to show.
25929      */
25930     setHidden : function(hidden){
25931         this.hidden = hidden;
25932         this.pnode.setStyle("display", hidden ? "none" : "");
25933     },
25934
25935     /**
25936      * Returns true if this tab is "hidden"
25937      * @return {Boolean}
25938      */
25939     isHidden : function(){
25940         return this.hidden;
25941     },
25942
25943     /**
25944      * Returns the text for this tab
25945      * @return {String}
25946      */
25947     getText : function(){
25948         return this.text;
25949     },
25950
25951     autoSize : function(){
25952         //this.el.beginMeasure();
25953         this.textEl.setWidth(1);
25954         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25955         //this.el.endMeasure();
25956     },
25957
25958     /**
25959      * Sets the text for the tab (Note: this also sets the tooltip text)
25960      * @param {String} text The tab's text and tooltip
25961      */
25962     setText : function(text){
25963         this.text = text;
25964         this.textEl.update(text);
25965         this.setTooltip(text);
25966         if(!this.tabPanel.resizeTabs){
25967             this.autoSize();
25968         }
25969     },
25970     /**
25971      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25972      */
25973     activate : function(){
25974         this.tabPanel.activate(this.id);
25975     },
25976
25977     /**
25978      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25979      */
25980     disable : function(){
25981         if(this.tabPanel.active != this){
25982             this.disabled = true;
25983             this.pnode.addClass("disabled");
25984         }
25985     },
25986
25987     /**
25988      * Enables this TabPanelItem if it was previously disabled.
25989      */
25990     enable : function(){
25991         this.disabled = false;
25992         this.pnode.removeClass("disabled");
25993     },
25994
25995     /**
25996      * Sets the content for this TabPanelItem.
25997      * @param {String} content The content
25998      * @param {Boolean} loadScripts true to look for and load scripts
25999      */
26000     setContent : function(content, loadScripts){
26001         this.bodyEl.update(content, loadScripts);
26002     },
26003
26004     /**
26005      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26006      * @return {Roo.UpdateManager} The UpdateManager
26007      */
26008     getUpdateManager : function(){
26009         return this.bodyEl.getUpdateManager();
26010     },
26011
26012     /**
26013      * Set a URL to be used to load the content for this TabPanelItem.
26014      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26015      * @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)
26016      * @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)
26017      * @return {Roo.UpdateManager} The UpdateManager
26018      */
26019     setUrl : function(url, params, loadOnce){
26020         if(this.refreshDelegate){
26021             this.un('activate', this.refreshDelegate);
26022         }
26023         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26024         this.on("activate", this.refreshDelegate);
26025         return this.bodyEl.getUpdateManager();
26026     },
26027
26028     /** @private */
26029     _handleRefresh : function(url, params, loadOnce){
26030         if(!loadOnce || !this.loaded){
26031             var updater = this.bodyEl.getUpdateManager();
26032             updater.update(url, params, this._setLoaded.createDelegate(this));
26033         }
26034     },
26035
26036     /**
26037      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26038      *   Will fail silently if the setUrl method has not been called.
26039      *   This does not activate the panel, just updates its content.
26040      */
26041     refresh : function(){
26042         if(this.refreshDelegate){
26043            this.loaded = false;
26044            this.refreshDelegate();
26045         }
26046     },
26047
26048     /** @private */
26049     _setLoaded : function(){
26050         this.loaded = true;
26051     },
26052
26053     /** @private */
26054     closeClick : function(e){
26055         var o = {};
26056         e.stopEvent();
26057         this.fireEvent("beforeclose", this, o);
26058         if(o.cancel !== true){
26059             this.tabPanel.removeTab(this.id);
26060         }
26061     },
26062     /**
26063      * The text displayed in the tooltip for the close icon.
26064      * @type String
26065      */
26066     closeText : "Close this tab"
26067 });
26068
26069 /** @private */
26070 Roo.TabPanel.prototype.createStrip = function(container){
26071     var strip = document.createElement("div");
26072     strip.className = "x-tabs-wrap";
26073     container.appendChild(strip);
26074     return strip;
26075 };
26076 /** @private */
26077 Roo.TabPanel.prototype.createStripList = function(strip){
26078     // div wrapper for retard IE
26079     // returns the "tr" element.
26080     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26081         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26082         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26083     return strip.firstChild.firstChild.firstChild.firstChild;
26084 };
26085 /** @private */
26086 Roo.TabPanel.prototype.createBody = function(container){
26087     var body = document.createElement("div");
26088     Roo.id(body, "tab-body");
26089     Roo.fly(body).addClass("x-tabs-body");
26090     container.appendChild(body);
26091     return body;
26092 };
26093 /** @private */
26094 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26095     var body = Roo.getDom(id);
26096     if(!body){
26097         body = document.createElement("div");
26098         body.id = id;
26099     }
26100     Roo.fly(body).addClass("x-tabs-item-body");
26101     bodyEl.insertBefore(body, bodyEl.firstChild);
26102     return body;
26103 };
26104 /** @private */
26105 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26106     var td = document.createElement("td");
26107     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26108     //stripEl.appendChild(td);
26109     if(closable){
26110         td.className = "x-tabs-closable";
26111         if(!this.closeTpl){
26112             this.closeTpl = new Roo.Template(
26113                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26114                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26115                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26116             );
26117         }
26118         var el = this.closeTpl.overwrite(td, {"text": text});
26119         var close = el.getElementsByTagName("div")[0];
26120         var inner = el.getElementsByTagName("em")[0];
26121         return {"el": el, "close": close, "inner": inner};
26122     } else {
26123         if(!this.tabTpl){
26124             this.tabTpl = new Roo.Template(
26125                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26126                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26127             );
26128         }
26129         var el = this.tabTpl.overwrite(td, {"text": text});
26130         var inner = el.getElementsByTagName("em")[0];
26131         return {"el": el, "inner": inner};
26132     }
26133 };/*
26134  * Based on:
26135  * Ext JS Library 1.1.1
26136  * Copyright(c) 2006-2007, Ext JS, LLC.
26137  *
26138  * Originally Released Under LGPL - original licence link has changed is not relivant.
26139  *
26140  * Fork - LGPL
26141  * <script type="text/javascript">
26142  */
26143
26144 /**
26145  * @class Roo.Button
26146  * @extends Roo.util.Observable
26147  * Simple Button class
26148  * @cfg {String} text The button text
26149  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26150  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26151  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26152  * @cfg {Object} scope The scope of the handler
26153  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26154  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26155  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26156  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26157  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26158  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26159    applies if enableToggle = true)
26160  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26161  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26162   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26163  * @constructor
26164  * Create a new button
26165  * @param {Object} config The config object
26166  */
26167 Roo.Button = function(renderTo, config)
26168 {
26169     if (!config) {
26170         config = renderTo;
26171         renderTo = config.renderTo || false;
26172     }
26173     
26174     Roo.apply(this, config);
26175     this.addEvents({
26176         /**
26177              * @event click
26178              * Fires when this button is clicked
26179              * @param {Button} this
26180              * @param {EventObject} e The click event
26181              */
26182             "click" : true,
26183         /**
26184              * @event toggle
26185              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26186              * @param {Button} this
26187              * @param {Boolean} pressed
26188              */
26189             "toggle" : true,
26190         /**
26191              * @event mouseover
26192              * Fires when the mouse hovers over the button
26193              * @param {Button} this
26194              * @param {Event} e The event object
26195              */
26196         'mouseover' : true,
26197         /**
26198              * @event mouseout
26199              * Fires when the mouse exits the button
26200              * @param {Button} this
26201              * @param {Event} e The event object
26202              */
26203         'mouseout': true,
26204          /**
26205              * @event render
26206              * Fires when the button is rendered
26207              * @param {Button} this
26208              */
26209         'render': true
26210     });
26211     if(this.menu){
26212         this.menu = Roo.menu.MenuMgr.get(this.menu);
26213     }
26214     // register listeners first!!  - so render can be captured..
26215     Roo.util.Observable.call(this);
26216     if(renderTo){
26217         this.render(renderTo);
26218     }
26219     
26220   
26221 };
26222
26223 Roo.extend(Roo.Button, Roo.util.Observable, {
26224     /**
26225      * 
26226      */
26227     
26228     /**
26229      * Read-only. True if this button is hidden
26230      * @type Boolean
26231      */
26232     hidden : false,
26233     /**
26234      * Read-only. True if this button is disabled
26235      * @type Boolean
26236      */
26237     disabled : false,
26238     /**
26239      * Read-only. True if this button is pressed (only if enableToggle = true)
26240      * @type Boolean
26241      */
26242     pressed : false,
26243
26244     /**
26245      * @cfg {Number} tabIndex 
26246      * The DOM tabIndex for this button (defaults to undefined)
26247      */
26248     tabIndex : undefined,
26249
26250     /**
26251      * @cfg {Boolean} enableToggle
26252      * True to enable pressed/not pressed toggling (defaults to false)
26253      */
26254     enableToggle: false,
26255     /**
26256      * @cfg {Mixed} menu
26257      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26258      */
26259     menu : undefined,
26260     /**
26261      * @cfg {String} menuAlign
26262      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26263      */
26264     menuAlign : "tl-bl?",
26265
26266     /**
26267      * @cfg {String} iconCls
26268      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26269      */
26270     iconCls : undefined,
26271     /**
26272      * @cfg {String} type
26273      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26274      */
26275     type : 'button',
26276
26277     // private
26278     menuClassTarget: 'tr',
26279
26280     /**
26281      * @cfg {String} clickEvent
26282      * The type of event to map to the button's event handler (defaults to 'click')
26283      */
26284     clickEvent : 'click',
26285
26286     /**
26287      * @cfg {Boolean} handleMouseEvents
26288      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26289      */
26290     handleMouseEvents : true,
26291
26292     /**
26293      * @cfg {String} tooltipType
26294      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26295      */
26296     tooltipType : 'qtip',
26297
26298     /**
26299      * @cfg {String} cls
26300      * A CSS class to apply to the button's main element.
26301      */
26302     
26303     /**
26304      * @cfg {Roo.Template} template (Optional)
26305      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26306      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26307      * require code modifications if required elements (e.g. a button) aren't present.
26308      */
26309
26310     // private
26311     render : function(renderTo){
26312         var btn;
26313         if(this.hideParent){
26314             this.parentEl = Roo.get(renderTo);
26315         }
26316         if(!this.dhconfig){
26317             if(!this.template){
26318                 if(!Roo.Button.buttonTemplate){
26319                     // hideous table template
26320                     Roo.Button.buttonTemplate = new Roo.Template(
26321                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26322                         '<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>',
26323                         "</tr></tbody></table>");
26324                 }
26325                 this.template = Roo.Button.buttonTemplate;
26326             }
26327             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26328             var btnEl = btn.child("button:first");
26329             btnEl.on('focus', this.onFocus, this);
26330             btnEl.on('blur', this.onBlur, this);
26331             if(this.cls){
26332                 btn.addClass(this.cls);
26333             }
26334             if(this.icon){
26335                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26336             }
26337             if(this.iconCls){
26338                 btnEl.addClass(this.iconCls);
26339                 if(!this.cls){
26340                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26341                 }
26342             }
26343             if(this.tabIndex !== undefined){
26344                 btnEl.dom.tabIndex = this.tabIndex;
26345             }
26346             if(this.tooltip){
26347                 if(typeof this.tooltip == 'object'){
26348                     Roo.QuickTips.tips(Roo.apply({
26349                           target: btnEl.id
26350                     }, this.tooltip));
26351                 } else {
26352                     btnEl.dom[this.tooltipType] = this.tooltip;
26353                 }
26354             }
26355         }else{
26356             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26357         }
26358         this.el = btn;
26359         if(this.id){
26360             this.el.dom.id = this.el.id = this.id;
26361         }
26362         if(this.menu){
26363             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26364             this.menu.on("show", this.onMenuShow, this);
26365             this.menu.on("hide", this.onMenuHide, this);
26366         }
26367         btn.addClass("x-btn");
26368         if(Roo.isIE && !Roo.isIE7){
26369             this.autoWidth.defer(1, this);
26370         }else{
26371             this.autoWidth();
26372         }
26373         if(this.handleMouseEvents){
26374             btn.on("mouseover", this.onMouseOver, this);
26375             btn.on("mouseout", this.onMouseOut, this);
26376             btn.on("mousedown", this.onMouseDown, this);
26377         }
26378         btn.on(this.clickEvent, this.onClick, this);
26379         //btn.on("mouseup", this.onMouseUp, this);
26380         if(this.hidden){
26381             this.hide();
26382         }
26383         if(this.disabled){
26384             this.disable();
26385         }
26386         Roo.ButtonToggleMgr.register(this);
26387         if(this.pressed){
26388             this.el.addClass("x-btn-pressed");
26389         }
26390         if(this.repeat){
26391             var repeater = new Roo.util.ClickRepeater(btn,
26392                 typeof this.repeat == "object" ? this.repeat : {}
26393             );
26394             repeater.on("click", this.onClick,  this);
26395         }
26396         
26397         this.fireEvent('render', this);
26398         
26399     },
26400     /**
26401      * Returns the button's underlying element
26402      * @return {Roo.Element} The element
26403      */
26404     getEl : function(){
26405         return this.el;  
26406     },
26407     
26408     /**
26409      * Destroys this Button and removes any listeners.
26410      */
26411     destroy : function(){
26412         Roo.ButtonToggleMgr.unregister(this);
26413         this.el.removeAllListeners();
26414         this.purgeListeners();
26415         this.el.remove();
26416     },
26417
26418     // private
26419     autoWidth : function(){
26420         if(this.el){
26421             this.el.setWidth("auto");
26422             if(Roo.isIE7 && Roo.isStrict){
26423                 var ib = this.el.child('button');
26424                 if(ib && ib.getWidth() > 20){
26425                     ib.clip();
26426                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26427                 }
26428             }
26429             if(this.minWidth){
26430                 if(this.hidden){
26431                     this.el.beginMeasure();
26432                 }
26433                 if(this.el.getWidth() < this.minWidth){
26434                     this.el.setWidth(this.minWidth);
26435                 }
26436                 if(this.hidden){
26437                     this.el.endMeasure();
26438                 }
26439             }
26440         }
26441     },
26442
26443     /**
26444      * Assigns this button's click handler
26445      * @param {Function} handler The function to call when the button is clicked
26446      * @param {Object} scope (optional) Scope for the function passed in
26447      */
26448     setHandler : function(handler, scope){
26449         this.handler = handler;
26450         this.scope = scope;  
26451     },
26452     
26453     /**
26454      * Sets this button's text
26455      * @param {String} text The button text
26456      */
26457     setText : function(text){
26458         this.text = text;
26459         if(this.el){
26460             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26461         }
26462         this.autoWidth();
26463     },
26464     
26465     /**
26466      * Gets the text for this button
26467      * @return {String} The button text
26468      */
26469     getText : function(){
26470         return this.text;  
26471     },
26472     
26473     /**
26474      * Show this button
26475      */
26476     show: function(){
26477         this.hidden = false;
26478         if(this.el){
26479             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26480         }
26481     },
26482     
26483     /**
26484      * Hide this button
26485      */
26486     hide: function(){
26487         this.hidden = true;
26488         if(this.el){
26489             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26490         }
26491     },
26492     
26493     /**
26494      * Convenience function for boolean show/hide
26495      * @param {Boolean} visible True to show, false to hide
26496      */
26497     setVisible: function(visible){
26498         if(visible) {
26499             this.show();
26500         }else{
26501             this.hide();
26502         }
26503     },
26504     
26505     /**
26506      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26507      * @param {Boolean} state (optional) Force a particular state
26508      */
26509     toggle : function(state){
26510         state = state === undefined ? !this.pressed : state;
26511         if(state != this.pressed){
26512             if(state){
26513                 this.el.addClass("x-btn-pressed");
26514                 this.pressed = true;
26515                 this.fireEvent("toggle", this, true);
26516             }else{
26517                 this.el.removeClass("x-btn-pressed");
26518                 this.pressed = false;
26519                 this.fireEvent("toggle", this, false);
26520             }
26521             if(this.toggleHandler){
26522                 this.toggleHandler.call(this.scope || this, this, state);
26523             }
26524         }
26525     },
26526     
26527     /**
26528      * Focus the button
26529      */
26530     focus : function(){
26531         this.el.child('button:first').focus();
26532     },
26533     
26534     /**
26535      * Disable this button
26536      */
26537     disable : function(){
26538         if(this.el){
26539             this.el.addClass("x-btn-disabled");
26540         }
26541         this.disabled = true;
26542     },
26543     
26544     /**
26545      * Enable this button
26546      */
26547     enable : function(){
26548         if(this.el){
26549             this.el.removeClass("x-btn-disabled");
26550         }
26551         this.disabled = false;
26552     },
26553
26554     /**
26555      * Convenience function for boolean enable/disable
26556      * @param {Boolean} enabled True to enable, false to disable
26557      */
26558     setDisabled : function(v){
26559         this[v !== true ? "enable" : "disable"]();
26560     },
26561
26562     // private
26563     onClick : function(e){
26564         if(e){
26565             e.preventDefault();
26566         }
26567         if(e.button != 0){
26568             return;
26569         }
26570         if(!this.disabled){
26571             if(this.enableToggle){
26572                 this.toggle();
26573             }
26574             if(this.menu && !this.menu.isVisible()){
26575                 this.menu.show(this.el, this.menuAlign);
26576             }
26577             this.fireEvent("click", this, e);
26578             if(this.handler){
26579                 this.el.removeClass("x-btn-over");
26580                 this.handler.call(this.scope || this, this, e);
26581             }
26582         }
26583     },
26584     // private
26585     onMouseOver : function(e){
26586         if(!this.disabled){
26587             this.el.addClass("x-btn-over");
26588             this.fireEvent('mouseover', this, e);
26589         }
26590     },
26591     // private
26592     onMouseOut : function(e){
26593         if(!e.within(this.el,  true)){
26594             this.el.removeClass("x-btn-over");
26595             this.fireEvent('mouseout', this, e);
26596         }
26597     },
26598     // private
26599     onFocus : function(e){
26600         if(!this.disabled){
26601             this.el.addClass("x-btn-focus");
26602         }
26603     },
26604     // private
26605     onBlur : function(e){
26606         this.el.removeClass("x-btn-focus");
26607     },
26608     // private
26609     onMouseDown : function(e){
26610         if(!this.disabled && e.button == 0){
26611             this.el.addClass("x-btn-click");
26612             Roo.get(document).on('mouseup', this.onMouseUp, this);
26613         }
26614     },
26615     // private
26616     onMouseUp : function(e){
26617         if(e.button == 0){
26618             this.el.removeClass("x-btn-click");
26619             Roo.get(document).un('mouseup', this.onMouseUp, this);
26620         }
26621     },
26622     // private
26623     onMenuShow : function(e){
26624         this.el.addClass("x-btn-menu-active");
26625     },
26626     // private
26627     onMenuHide : function(e){
26628         this.el.removeClass("x-btn-menu-active");
26629     }   
26630 });
26631
26632 // Private utility class used by Button
26633 Roo.ButtonToggleMgr = function(){
26634    var groups = {};
26635    
26636    function toggleGroup(btn, state){
26637        if(state){
26638            var g = groups[btn.toggleGroup];
26639            for(var i = 0, l = g.length; i < l; i++){
26640                if(g[i] != btn){
26641                    g[i].toggle(false);
26642                }
26643            }
26644        }
26645    }
26646    
26647    return {
26648        register : function(btn){
26649            if(!btn.toggleGroup){
26650                return;
26651            }
26652            var g = groups[btn.toggleGroup];
26653            if(!g){
26654                g = groups[btn.toggleGroup] = [];
26655            }
26656            g.push(btn);
26657            btn.on("toggle", toggleGroup);
26658        },
26659        
26660        unregister : function(btn){
26661            if(!btn.toggleGroup){
26662                return;
26663            }
26664            var g = groups[btn.toggleGroup];
26665            if(g){
26666                g.remove(btn);
26667                btn.un("toggle", toggleGroup);
26668            }
26669        }
26670    };
26671 }();/*
26672  * Based on:
26673  * Ext JS Library 1.1.1
26674  * Copyright(c) 2006-2007, Ext JS, LLC.
26675  *
26676  * Originally Released Under LGPL - original licence link has changed is not relivant.
26677  *
26678  * Fork - LGPL
26679  * <script type="text/javascript">
26680  */
26681  
26682 /**
26683  * @class Roo.SplitButton
26684  * @extends Roo.Button
26685  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26686  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26687  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26688  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26689  * @cfg {String} arrowTooltip The title attribute of the arrow
26690  * @constructor
26691  * Create a new menu button
26692  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26693  * @param {Object} config The config object
26694  */
26695 Roo.SplitButton = function(renderTo, config){
26696     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26697     /**
26698      * @event arrowclick
26699      * Fires when this button's arrow is clicked
26700      * @param {SplitButton} this
26701      * @param {EventObject} e The click event
26702      */
26703     this.addEvents({"arrowclick":true});
26704 };
26705
26706 Roo.extend(Roo.SplitButton, Roo.Button, {
26707     render : function(renderTo){
26708         // this is one sweet looking template!
26709         var tpl = new Roo.Template(
26710             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26711             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26712             '<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>',
26713             "</tbody></table></td><td>",
26714             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26715             '<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>',
26716             "</tbody></table></td></tr></table>"
26717         );
26718         var btn = tpl.append(renderTo, [this.text, this.type], true);
26719         var btnEl = btn.child("button");
26720         if(this.cls){
26721             btn.addClass(this.cls);
26722         }
26723         if(this.icon){
26724             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26725         }
26726         if(this.iconCls){
26727             btnEl.addClass(this.iconCls);
26728             if(!this.cls){
26729                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26730             }
26731         }
26732         this.el = btn;
26733         if(this.handleMouseEvents){
26734             btn.on("mouseover", this.onMouseOver, this);
26735             btn.on("mouseout", this.onMouseOut, this);
26736             btn.on("mousedown", this.onMouseDown, this);
26737             btn.on("mouseup", this.onMouseUp, this);
26738         }
26739         btn.on(this.clickEvent, this.onClick, this);
26740         if(this.tooltip){
26741             if(typeof this.tooltip == 'object'){
26742                 Roo.QuickTips.tips(Roo.apply({
26743                       target: btnEl.id
26744                 }, this.tooltip));
26745             } else {
26746                 btnEl.dom[this.tooltipType] = this.tooltip;
26747             }
26748         }
26749         if(this.arrowTooltip){
26750             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26751         }
26752         if(this.hidden){
26753             this.hide();
26754         }
26755         if(this.disabled){
26756             this.disable();
26757         }
26758         if(this.pressed){
26759             this.el.addClass("x-btn-pressed");
26760         }
26761         if(Roo.isIE && !Roo.isIE7){
26762             this.autoWidth.defer(1, this);
26763         }else{
26764             this.autoWidth();
26765         }
26766         if(this.menu){
26767             this.menu.on("show", this.onMenuShow, this);
26768             this.menu.on("hide", this.onMenuHide, this);
26769         }
26770         this.fireEvent('render', this);
26771     },
26772
26773     // private
26774     autoWidth : function(){
26775         if(this.el){
26776             var tbl = this.el.child("table:first");
26777             var tbl2 = this.el.child("table:last");
26778             this.el.setWidth("auto");
26779             tbl.setWidth("auto");
26780             if(Roo.isIE7 && Roo.isStrict){
26781                 var ib = this.el.child('button:first');
26782                 if(ib && ib.getWidth() > 20){
26783                     ib.clip();
26784                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26785                 }
26786             }
26787             if(this.minWidth){
26788                 if(this.hidden){
26789                     this.el.beginMeasure();
26790                 }
26791                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26792                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26793                 }
26794                 if(this.hidden){
26795                     this.el.endMeasure();
26796                 }
26797             }
26798             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26799         } 
26800     },
26801     /**
26802      * Sets this button's click handler
26803      * @param {Function} handler The function to call when the button is clicked
26804      * @param {Object} scope (optional) Scope for the function passed above
26805      */
26806     setHandler : function(handler, scope){
26807         this.handler = handler;
26808         this.scope = scope;  
26809     },
26810     
26811     /**
26812      * Sets this button's arrow click handler
26813      * @param {Function} handler The function to call when the arrow is clicked
26814      * @param {Object} scope (optional) Scope for the function passed above
26815      */
26816     setArrowHandler : function(handler, scope){
26817         this.arrowHandler = handler;
26818         this.scope = scope;  
26819     },
26820     
26821     /**
26822      * Focus the button
26823      */
26824     focus : function(){
26825         if(this.el){
26826             this.el.child("button:first").focus();
26827         }
26828     },
26829
26830     // private
26831     onClick : function(e){
26832         e.preventDefault();
26833         if(!this.disabled){
26834             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26835                 if(this.menu && !this.menu.isVisible()){
26836                     this.menu.show(this.el, this.menuAlign);
26837                 }
26838                 this.fireEvent("arrowclick", this, e);
26839                 if(this.arrowHandler){
26840                     this.arrowHandler.call(this.scope || this, this, e);
26841                 }
26842             }else{
26843                 this.fireEvent("click", this, e);
26844                 if(this.handler){
26845                     this.handler.call(this.scope || this, this, e);
26846                 }
26847             }
26848         }
26849     },
26850     // private
26851     onMouseDown : function(e){
26852         if(!this.disabled){
26853             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26854         }
26855     },
26856     // private
26857     onMouseUp : function(e){
26858         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26859     }   
26860 });
26861
26862
26863 // backwards compat
26864 Roo.MenuButton = Roo.SplitButton;/*
26865  * Based on:
26866  * Ext JS Library 1.1.1
26867  * Copyright(c) 2006-2007, Ext JS, LLC.
26868  *
26869  * Originally Released Under LGPL - original licence link has changed is not relivant.
26870  *
26871  * Fork - LGPL
26872  * <script type="text/javascript">
26873  */
26874
26875 /**
26876  * @class Roo.Toolbar
26877  * Basic Toolbar class.
26878  * @constructor
26879  * Creates a new Toolbar
26880  * @param {Object} container The config object
26881  */ 
26882 Roo.Toolbar = function(container, buttons, config)
26883 {
26884     /// old consturctor format still supported..
26885     if(container instanceof Array){ // omit the container for later rendering
26886         buttons = container;
26887         config = buttons;
26888         container = null;
26889     }
26890     if (typeof(container) == 'object' && container.xtype) {
26891         config = container;
26892         container = config.container;
26893         buttons = config.buttons || []; // not really - use items!!
26894     }
26895     var xitems = [];
26896     if (config && config.items) {
26897         xitems = config.items;
26898         delete config.items;
26899     }
26900     Roo.apply(this, config);
26901     this.buttons = buttons;
26902     
26903     if(container){
26904         this.render(container);
26905     }
26906     this.xitems = xitems;
26907     Roo.each(xitems, function(b) {
26908         this.add(b);
26909     }, this);
26910     
26911 };
26912
26913 Roo.Toolbar.prototype = {
26914     /**
26915      * @cfg {Array} items
26916      * array of button configs or elements to add (will be converted to a MixedCollection)
26917      */
26918     
26919     /**
26920      * @cfg {String/HTMLElement/Element} container
26921      * The id or element that will contain the toolbar
26922      */
26923     // private
26924     render : function(ct){
26925         this.el = Roo.get(ct);
26926         if(this.cls){
26927             this.el.addClass(this.cls);
26928         }
26929         // using a table allows for vertical alignment
26930         // 100% width is needed by Safari...
26931         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26932         this.tr = this.el.child("tr", true);
26933         var autoId = 0;
26934         this.items = new Roo.util.MixedCollection(false, function(o){
26935             return o.id || ("item" + (++autoId));
26936         });
26937         if(this.buttons){
26938             this.add.apply(this, this.buttons);
26939             delete this.buttons;
26940         }
26941     },
26942
26943     /**
26944      * Adds element(s) to the toolbar -- this function takes a variable number of 
26945      * arguments of mixed type and adds them to the toolbar.
26946      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26947      * <ul>
26948      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26949      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26950      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26951      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26952      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26953      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26954      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26955      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26956      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26957      * </ul>
26958      * @param {Mixed} arg2
26959      * @param {Mixed} etc.
26960      */
26961     add : function(){
26962         var a = arguments, l = a.length;
26963         for(var i = 0; i < l; i++){
26964             this._add(a[i]);
26965         }
26966     },
26967     // private..
26968     _add : function(el) {
26969         
26970         if (el.xtype) {
26971             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26972         }
26973         
26974         if (el.applyTo){ // some kind of form field
26975             return this.addField(el);
26976         } 
26977         if (el.render){ // some kind of Toolbar.Item
26978             return this.addItem(el);
26979         }
26980         if (typeof el == "string"){ // string
26981             if(el == "separator" || el == "-"){
26982                 return this.addSeparator();
26983             }
26984             if (el == " "){
26985                 return this.addSpacer();
26986             }
26987             if(el == "->"){
26988                 return this.addFill();
26989             }
26990             return this.addText(el);
26991             
26992         }
26993         if(el.tagName){ // element
26994             return this.addElement(el);
26995         }
26996         if(typeof el == "object"){ // must be button config?
26997             return this.addButton(el);
26998         }
26999         // and now what?!?!
27000         return false;
27001         
27002     },
27003     
27004     /**
27005      * Add an Xtype element
27006      * @param {Object} xtype Xtype Object
27007      * @return {Object} created Object
27008      */
27009     addxtype : function(e){
27010         return this.add(e);  
27011     },
27012     
27013     /**
27014      * Returns the Element for this toolbar.
27015      * @return {Roo.Element}
27016      */
27017     getEl : function(){
27018         return this.el;  
27019     },
27020     
27021     /**
27022      * Adds a separator
27023      * @return {Roo.Toolbar.Item} The separator item
27024      */
27025     addSeparator : function(){
27026         return this.addItem(new Roo.Toolbar.Separator());
27027     },
27028
27029     /**
27030      * Adds a spacer element
27031      * @return {Roo.Toolbar.Spacer} The spacer item
27032      */
27033     addSpacer : function(){
27034         return this.addItem(new Roo.Toolbar.Spacer());
27035     },
27036
27037     /**
27038      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27039      * @return {Roo.Toolbar.Fill} The fill item
27040      */
27041     addFill : function(){
27042         return this.addItem(new Roo.Toolbar.Fill());
27043     },
27044
27045     /**
27046      * Adds any standard HTML element to the toolbar
27047      * @param {String/HTMLElement/Element} el The element or id of the element to add
27048      * @return {Roo.Toolbar.Item} The element's item
27049      */
27050     addElement : function(el){
27051         return this.addItem(new Roo.Toolbar.Item(el));
27052     },
27053     /**
27054      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27055      * @type Roo.util.MixedCollection  
27056      */
27057     items : false,
27058      
27059     /**
27060      * Adds any Toolbar.Item or subclass
27061      * @param {Roo.Toolbar.Item} item
27062      * @return {Roo.Toolbar.Item} The item
27063      */
27064     addItem : function(item){
27065         var td = this.nextBlock();
27066         item.render(td);
27067         this.items.add(item);
27068         return item;
27069     },
27070     
27071     /**
27072      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27073      * @param {Object/Array} config A button config or array of configs
27074      * @return {Roo.Toolbar.Button/Array}
27075      */
27076     addButton : function(config){
27077         if(config instanceof Array){
27078             var buttons = [];
27079             for(var i = 0, len = config.length; i < len; i++) {
27080                 buttons.push(this.addButton(config[i]));
27081             }
27082             return buttons;
27083         }
27084         var b = config;
27085         if(!(config instanceof Roo.Toolbar.Button)){
27086             b = config.split ?
27087                 new Roo.Toolbar.SplitButton(config) :
27088                 new Roo.Toolbar.Button(config);
27089         }
27090         var td = this.nextBlock();
27091         b.render(td);
27092         this.items.add(b);
27093         return b;
27094     },
27095     
27096     /**
27097      * Adds text to the toolbar
27098      * @param {String} text The text to add
27099      * @return {Roo.Toolbar.Item} The element's item
27100      */
27101     addText : function(text){
27102         return this.addItem(new Roo.Toolbar.TextItem(text));
27103     },
27104     
27105     /**
27106      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27107      * @param {Number} index The index where the item is to be inserted
27108      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27109      * @return {Roo.Toolbar.Button/Item}
27110      */
27111     insertButton : function(index, item){
27112         if(item instanceof Array){
27113             var buttons = [];
27114             for(var i = 0, len = item.length; i < len; i++) {
27115                buttons.push(this.insertButton(index + i, item[i]));
27116             }
27117             return buttons;
27118         }
27119         if (!(item instanceof Roo.Toolbar.Button)){
27120            item = new Roo.Toolbar.Button(item);
27121         }
27122         var td = document.createElement("td");
27123         this.tr.insertBefore(td, this.tr.childNodes[index]);
27124         item.render(td);
27125         this.items.insert(index, item);
27126         return item;
27127     },
27128     
27129     /**
27130      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27131      * @param {Object} config
27132      * @return {Roo.Toolbar.Item} The element's item
27133      */
27134     addDom : function(config, returnEl){
27135         var td = this.nextBlock();
27136         Roo.DomHelper.overwrite(td, config);
27137         var ti = new Roo.Toolbar.Item(td.firstChild);
27138         ti.render(td);
27139         this.items.add(ti);
27140         return ti;
27141     },
27142
27143     /**
27144      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27145      * @type Roo.util.MixedCollection  
27146      */
27147     fields : false,
27148     
27149     /**
27150      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27151      * Note: the field should not have been rendered yet. For a field that has already been
27152      * rendered, use {@link #addElement}.
27153      * @param {Roo.form.Field} field
27154      * @return {Roo.ToolbarItem}
27155      */
27156      
27157       
27158     addField : function(field) {
27159         if (!this.fields) {
27160             var autoId = 0;
27161             this.fields = new Roo.util.MixedCollection(false, function(o){
27162                 return o.id || ("item" + (++autoId));
27163             });
27164
27165         }
27166         
27167         var td = this.nextBlock();
27168         field.render(td);
27169         var ti = new Roo.Toolbar.Item(td.firstChild);
27170         ti.render(td);
27171         this.items.add(ti);
27172         this.fields.add(field);
27173         return ti;
27174     },
27175     /**
27176      * Hide the toolbar
27177      * @method hide
27178      */
27179      
27180       
27181     hide : function()
27182     {
27183         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27184         this.el.child('div').hide();
27185     },
27186     /**
27187      * Show the toolbar
27188      * @method show
27189      */
27190     show : function()
27191     {
27192         this.el.child('div').show();
27193     },
27194       
27195     // private
27196     nextBlock : function(){
27197         var td = document.createElement("td");
27198         this.tr.appendChild(td);
27199         return td;
27200     },
27201
27202     // private
27203     destroy : function(){
27204         if(this.items){ // rendered?
27205             Roo.destroy.apply(Roo, this.items.items);
27206         }
27207         if(this.fields){ // rendered?
27208             Roo.destroy.apply(Roo, this.fields.items);
27209         }
27210         Roo.Element.uncache(this.el, this.tr);
27211     }
27212 };
27213
27214 /**
27215  * @class Roo.Toolbar.Item
27216  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27217  * @constructor
27218  * Creates a new Item
27219  * @param {HTMLElement} el 
27220  */
27221 Roo.Toolbar.Item = function(el){
27222     this.el = Roo.getDom(el);
27223     this.id = Roo.id(this.el);
27224     this.hidden = false;
27225 };
27226
27227 Roo.Toolbar.Item.prototype = {
27228     
27229     /**
27230      * Get this item's HTML Element
27231      * @return {HTMLElement}
27232      */
27233     getEl : function(){
27234        return this.el;  
27235     },
27236
27237     // private
27238     render : function(td){
27239         this.td = td;
27240         td.appendChild(this.el);
27241     },
27242     
27243     /**
27244      * Removes and destroys this item.
27245      */
27246     destroy : function(){
27247         this.td.parentNode.removeChild(this.td);
27248     },
27249     
27250     /**
27251      * Shows this item.
27252      */
27253     show: function(){
27254         this.hidden = false;
27255         this.td.style.display = "";
27256     },
27257     
27258     /**
27259      * Hides this item.
27260      */
27261     hide: function(){
27262         this.hidden = true;
27263         this.td.style.display = "none";
27264     },
27265     
27266     /**
27267      * Convenience function for boolean show/hide.
27268      * @param {Boolean} visible true to show/false to hide
27269      */
27270     setVisible: function(visible){
27271         if(visible) {
27272             this.show();
27273         }else{
27274             this.hide();
27275         }
27276     },
27277     
27278     /**
27279      * Try to focus this item.
27280      */
27281     focus : function(){
27282         Roo.fly(this.el).focus();
27283     },
27284     
27285     /**
27286      * Disables this item.
27287      */
27288     disable : function(){
27289         Roo.fly(this.td).addClass("x-item-disabled");
27290         this.disabled = true;
27291         this.el.disabled = true;
27292     },
27293     
27294     /**
27295      * Enables this item.
27296      */
27297     enable : function(){
27298         Roo.fly(this.td).removeClass("x-item-disabled");
27299         this.disabled = false;
27300         this.el.disabled = false;
27301     }
27302 };
27303
27304
27305 /**
27306  * @class Roo.Toolbar.Separator
27307  * @extends Roo.Toolbar.Item
27308  * A simple toolbar separator class
27309  * @constructor
27310  * Creates a new Separator
27311  */
27312 Roo.Toolbar.Separator = function(){
27313     var s = document.createElement("span");
27314     s.className = "ytb-sep";
27315     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27316 };
27317 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27318     enable:Roo.emptyFn,
27319     disable:Roo.emptyFn,
27320     focus:Roo.emptyFn
27321 });
27322
27323 /**
27324  * @class Roo.Toolbar.Spacer
27325  * @extends Roo.Toolbar.Item
27326  * A simple element that adds extra horizontal space to a toolbar.
27327  * @constructor
27328  * Creates a new Spacer
27329  */
27330 Roo.Toolbar.Spacer = function(){
27331     var s = document.createElement("div");
27332     s.className = "ytb-spacer";
27333     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27334 };
27335 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27336     enable:Roo.emptyFn,
27337     disable:Roo.emptyFn,
27338     focus:Roo.emptyFn
27339 });
27340
27341 /**
27342  * @class Roo.Toolbar.Fill
27343  * @extends Roo.Toolbar.Spacer
27344  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27345  * @constructor
27346  * Creates a new Spacer
27347  */
27348 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27349     // private
27350     render : function(td){
27351         td.style.width = '100%';
27352         Roo.Toolbar.Fill.superclass.render.call(this, td);
27353     }
27354 });
27355
27356 /**
27357  * @class Roo.Toolbar.TextItem
27358  * @extends Roo.Toolbar.Item
27359  * A simple class that renders text directly into a toolbar.
27360  * @constructor
27361  * Creates a new TextItem
27362  * @param {String} text
27363  */
27364 Roo.Toolbar.TextItem = function(text){
27365     if (typeof(text) == 'object') {
27366         text = text.text;
27367     }
27368     var s = document.createElement("span");
27369     s.className = "ytb-text";
27370     s.innerHTML = text;
27371     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27372 };
27373 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27374     enable:Roo.emptyFn,
27375     disable:Roo.emptyFn,
27376     focus:Roo.emptyFn
27377 });
27378
27379 /**
27380  * @class Roo.Toolbar.Button
27381  * @extends Roo.Button
27382  * A button that renders into a toolbar.
27383  * @constructor
27384  * Creates a new Button
27385  * @param {Object} config A standard {@link Roo.Button} config object
27386  */
27387 Roo.Toolbar.Button = function(config){
27388     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27389 };
27390 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27391     render : function(td){
27392         this.td = td;
27393         Roo.Toolbar.Button.superclass.render.call(this, td);
27394     },
27395     
27396     /**
27397      * Removes and destroys this button
27398      */
27399     destroy : function(){
27400         Roo.Toolbar.Button.superclass.destroy.call(this);
27401         this.td.parentNode.removeChild(this.td);
27402     },
27403     
27404     /**
27405      * Shows this button
27406      */
27407     show: function(){
27408         this.hidden = false;
27409         this.td.style.display = "";
27410     },
27411     
27412     /**
27413      * Hides this button
27414      */
27415     hide: function(){
27416         this.hidden = true;
27417         this.td.style.display = "none";
27418     },
27419
27420     /**
27421      * Disables this item
27422      */
27423     disable : function(){
27424         Roo.fly(this.td).addClass("x-item-disabled");
27425         this.disabled = true;
27426     },
27427
27428     /**
27429      * Enables this item
27430      */
27431     enable : function(){
27432         Roo.fly(this.td).removeClass("x-item-disabled");
27433         this.disabled = false;
27434     }
27435 });
27436 // backwards compat
27437 Roo.ToolbarButton = Roo.Toolbar.Button;
27438
27439 /**
27440  * @class Roo.Toolbar.SplitButton
27441  * @extends Roo.SplitButton
27442  * A menu button that renders into a toolbar.
27443  * @constructor
27444  * Creates a new SplitButton
27445  * @param {Object} config A standard {@link Roo.SplitButton} config object
27446  */
27447 Roo.Toolbar.SplitButton = function(config){
27448     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27449 };
27450 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27451     render : function(td){
27452         this.td = td;
27453         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27454     },
27455     
27456     /**
27457      * Removes and destroys this button
27458      */
27459     destroy : function(){
27460         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27461         this.td.parentNode.removeChild(this.td);
27462     },
27463     
27464     /**
27465      * Shows this button
27466      */
27467     show: function(){
27468         this.hidden = false;
27469         this.td.style.display = "";
27470     },
27471     
27472     /**
27473      * Hides this button
27474      */
27475     hide: function(){
27476         this.hidden = true;
27477         this.td.style.display = "none";
27478     }
27479 });
27480
27481 // backwards compat
27482 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27483  * Based on:
27484  * Ext JS Library 1.1.1
27485  * Copyright(c) 2006-2007, Ext JS, LLC.
27486  *
27487  * Originally Released Under LGPL - original licence link has changed is not relivant.
27488  *
27489  * Fork - LGPL
27490  * <script type="text/javascript">
27491  */
27492  
27493 /**
27494  * @class Roo.PagingToolbar
27495  * @extends Roo.Toolbar
27496  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27497  * @constructor
27498  * Create a new PagingToolbar
27499  * @param {Object} config The config object
27500  */
27501 Roo.PagingToolbar = function(el, ds, config)
27502 {
27503     // old args format still supported... - xtype is prefered..
27504     if (typeof(el) == 'object' && el.xtype) {
27505         // created from xtype...
27506         config = el;
27507         ds = el.dataSource;
27508         el = config.container;
27509     }
27510     var items = [];
27511     if (config.items) {
27512         items = config.items;
27513         config.items = [];
27514     }
27515     
27516     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27517     this.ds = ds;
27518     this.cursor = 0;
27519     this.renderButtons(this.el);
27520     this.bind(ds);
27521     
27522     // supprot items array.
27523    
27524     Roo.each(items, function(e) {
27525         this.add(Roo.factory(e));
27526     },this);
27527     
27528 };
27529
27530 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27531     /**
27532      * @cfg {Roo.data.Store} dataSource
27533      * The underlying data store providing the paged data
27534      */
27535     /**
27536      * @cfg {String/HTMLElement/Element} container
27537      * container The id or element that will contain the toolbar
27538      */
27539     /**
27540      * @cfg {Boolean} displayInfo
27541      * True to display the displayMsg (defaults to false)
27542      */
27543     /**
27544      * @cfg {Number} pageSize
27545      * The number of records to display per page (defaults to 20)
27546      */
27547     pageSize: 20,
27548     /**
27549      * @cfg {String} displayMsg
27550      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27551      */
27552     displayMsg : 'Displaying {0} - {1} of {2}',
27553     /**
27554      * @cfg {String} emptyMsg
27555      * The message to display when no records are found (defaults to "No data to display")
27556      */
27557     emptyMsg : 'No data to display',
27558     /**
27559      * Customizable piece of the default paging text (defaults to "Page")
27560      * @type String
27561      */
27562     beforePageText : "Page",
27563     /**
27564      * Customizable piece of the default paging text (defaults to "of %0")
27565      * @type String
27566      */
27567     afterPageText : "of {0}",
27568     /**
27569      * Customizable piece of the default paging text (defaults to "First Page")
27570      * @type String
27571      */
27572     firstText : "First Page",
27573     /**
27574      * Customizable piece of the default paging text (defaults to "Previous Page")
27575      * @type String
27576      */
27577     prevText : "Previous Page",
27578     /**
27579      * Customizable piece of the default paging text (defaults to "Next Page")
27580      * @type String
27581      */
27582     nextText : "Next Page",
27583     /**
27584      * Customizable piece of the default paging text (defaults to "Last Page")
27585      * @type String
27586      */
27587     lastText : "Last Page",
27588     /**
27589      * Customizable piece of the default paging text (defaults to "Refresh")
27590      * @type String
27591      */
27592     refreshText : "Refresh",
27593
27594     // private
27595     renderButtons : function(el){
27596         Roo.PagingToolbar.superclass.render.call(this, el);
27597         this.first = this.addButton({
27598             tooltip: this.firstText,
27599             cls: "x-btn-icon x-grid-page-first",
27600             disabled: true,
27601             handler: this.onClick.createDelegate(this, ["first"])
27602         });
27603         this.prev = this.addButton({
27604             tooltip: this.prevText,
27605             cls: "x-btn-icon x-grid-page-prev",
27606             disabled: true,
27607             handler: this.onClick.createDelegate(this, ["prev"])
27608         });
27609         //this.addSeparator();
27610         this.add(this.beforePageText);
27611         this.field = Roo.get(this.addDom({
27612            tag: "input",
27613            type: "text",
27614            size: "3",
27615            value: "1",
27616            cls: "x-grid-page-number"
27617         }).el);
27618         this.field.on("keydown", this.onPagingKeydown, this);
27619         this.field.on("focus", function(){this.dom.select();});
27620         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27621         this.field.setHeight(18);
27622         //this.addSeparator();
27623         this.next = this.addButton({
27624             tooltip: this.nextText,
27625             cls: "x-btn-icon x-grid-page-next",
27626             disabled: true,
27627             handler: this.onClick.createDelegate(this, ["next"])
27628         });
27629         this.last = this.addButton({
27630             tooltip: this.lastText,
27631             cls: "x-btn-icon x-grid-page-last",
27632             disabled: true,
27633             handler: this.onClick.createDelegate(this, ["last"])
27634         });
27635         //this.addSeparator();
27636         this.loading = this.addButton({
27637             tooltip: this.refreshText,
27638             cls: "x-btn-icon x-grid-loading",
27639             handler: this.onClick.createDelegate(this, ["refresh"])
27640         });
27641
27642         if(this.displayInfo){
27643             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27644         }
27645     },
27646
27647     // private
27648     updateInfo : function(){
27649         if(this.displayEl){
27650             var count = this.ds.getCount();
27651             var msg = count == 0 ?
27652                 this.emptyMsg :
27653                 String.format(
27654                     this.displayMsg,
27655                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27656                 );
27657             this.displayEl.update(msg);
27658         }
27659     },
27660
27661     // private
27662     onLoad : function(ds, r, o){
27663        this.cursor = o.params ? o.params.start : 0;
27664        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27665
27666        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27667        this.field.dom.value = ap;
27668        this.first.setDisabled(ap == 1);
27669        this.prev.setDisabled(ap == 1);
27670        this.next.setDisabled(ap == ps);
27671        this.last.setDisabled(ap == ps);
27672        this.loading.enable();
27673        this.updateInfo();
27674     },
27675
27676     // private
27677     getPageData : function(){
27678         var total = this.ds.getTotalCount();
27679         return {
27680             total : total,
27681             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27682             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27683         };
27684     },
27685
27686     // private
27687     onLoadError : function(){
27688         this.loading.enable();
27689     },
27690
27691     // private
27692     onPagingKeydown : function(e){
27693         var k = e.getKey();
27694         var d = this.getPageData();
27695         if(k == e.RETURN){
27696             var v = this.field.dom.value, pageNum;
27697             if(!v || isNaN(pageNum = parseInt(v, 10))){
27698                 this.field.dom.value = d.activePage;
27699                 return;
27700             }
27701             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27702             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27703             e.stopEvent();
27704         }
27705         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))
27706         {
27707           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27708           this.field.dom.value = pageNum;
27709           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27710           e.stopEvent();
27711         }
27712         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27713         {
27714           var v = this.field.dom.value, pageNum; 
27715           var increment = (e.shiftKey) ? 10 : 1;
27716           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27717             increment *= -1;
27718           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27719             this.field.dom.value = d.activePage;
27720             return;
27721           }
27722           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27723           {
27724             this.field.dom.value = parseInt(v, 10) + increment;
27725             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27726             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27727           }
27728           e.stopEvent();
27729         }
27730     },
27731
27732     // private
27733     beforeLoad : function(){
27734         if(this.loading){
27735             this.loading.disable();
27736         }
27737     },
27738
27739     // private
27740     onClick : function(which){
27741         var ds = this.ds;
27742         switch(which){
27743             case "first":
27744                 ds.load({params:{start: 0, limit: this.pageSize}});
27745             break;
27746             case "prev":
27747                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27748             break;
27749             case "next":
27750                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27751             break;
27752             case "last":
27753                 var total = ds.getTotalCount();
27754                 var extra = total % this.pageSize;
27755                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27756                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27757             break;
27758             case "refresh":
27759                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27760             break;
27761         }
27762     },
27763
27764     /**
27765      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27766      * @param {Roo.data.Store} store The data store to unbind
27767      */
27768     unbind : function(ds){
27769         ds.un("beforeload", this.beforeLoad, this);
27770         ds.un("load", this.onLoad, this);
27771         ds.un("loadexception", this.onLoadError, this);
27772         ds.un("remove", this.updateInfo, this);
27773         ds.un("add", this.updateInfo, this);
27774         this.ds = undefined;
27775     },
27776
27777     /**
27778      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27779      * @param {Roo.data.Store} store The data store to bind
27780      */
27781     bind : function(ds){
27782         ds.on("beforeload", this.beforeLoad, this);
27783         ds.on("load", this.onLoad, this);
27784         ds.on("loadexception", this.onLoadError, this);
27785         ds.on("remove", this.updateInfo, this);
27786         ds.on("add", this.updateInfo, this);
27787         this.ds = ds;
27788     }
27789 });/*
27790  * Based on:
27791  * Ext JS Library 1.1.1
27792  * Copyright(c) 2006-2007, Ext JS, LLC.
27793  *
27794  * Originally Released Under LGPL - original licence link has changed is not relivant.
27795  *
27796  * Fork - LGPL
27797  * <script type="text/javascript">
27798  */
27799
27800 /**
27801  * @class Roo.Resizable
27802  * @extends Roo.util.Observable
27803  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27804  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27805  * 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
27806  * the element will be wrapped for you automatically.</p>
27807  * <p>Here is the list of valid resize handles:</p>
27808  * <pre>
27809 Value   Description
27810 ------  -------------------
27811  'n'     north
27812  's'     south
27813  'e'     east
27814  'w'     west
27815  'nw'    northwest
27816  'sw'    southwest
27817  'se'    southeast
27818  'ne'    northeast
27819  'hd'    horizontal drag
27820  'all'   all
27821 </pre>
27822  * <p>Here's an example showing the creation of a typical Resizable:</p>
27823  * <pre><code>
27824 var resizer = new Roo.Resizable("element-id", {
27825     handles: 'all',
27826     minWidth: 200,
27827     minHeight: 100,
27828     maxWidth: 500,
27829     maxHeight: 400,
27830     pinned: true
27831 });
27832 resizer.on("resize", myHandler);
27833 </code></pre>
27834  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27835  * resizer.east.setDisplayed(false);</p>
27836  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27837  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27838  * resize operation's new size (defaults to [0, 0])
27839  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27840  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27841  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27842  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27843  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27844  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27845  * @cfg {Number} width The width of the element in pixels (defaults to null)
27846  * @cfg {Number} height The height of the element in pixels (defaults to null)
27847  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27848  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27849  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27850  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27851  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27852  * in favor of the handles config option (defaults to false)
27853  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27854  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27855  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27856  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27857  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27858  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27859  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27860  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27861  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27862  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27863  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27864  * @constructor
27865  * Create a new resizable component
27866  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27867  * @param {Object} config configuration options
27868   */
27869 Roo.Resizable = function(el, config)
27870 {
27871     this.el = Roo.get(el);
27872
27873     if(config && config.wrap){
27874         config.resizeChild = this.el;
27875         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27876         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27877         this.el.setStyle("overflow", "hidden");
27878         this.el.setPositioning(config.resizeChild.getPositioning());
27879         config.resizeChild.clearPositioning();
27880         if(!config.width || !config.height){
27881             var csize = config.resizeChild.getSize();
27882             this.el.setSize(csize.width, csize.height);
27883         }
27884         if(config.pinned && !config.adjustments){
27885             config.adjustments = "auto";
27886         }
27887     }
27888
27889     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27890     this.proxy.unselectable();
27891     this.proxy.enableDisplayMode('block');
27892
27893     Roo.apply(this, config);
27894
27895     if(this.pinned){
27896         this.disableTrackOver = true;
27897         this.el.addClass("x-resizable-pinned");
27898     }
27899     // if the element isn't positioned, make it relative
27900     var position = this.el.getStyle("position");
27901     if(position != "absolute" && position != "fixed"){
27902         this.el.setStyle("position", "relative");
27903     }
27904     if(!this.handles){ // no handles passed, must be legacy style
27905         this.handles = 's,e,se';
27906         if(this.multiDirectional){
27907             this.handles += ',n,w';
27908         }
27909     }
27910     if(this.handles == "all"){
27911         this.handles = "n s e w ne nw se sw";
27912     }
27913     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27914     var ps = Roo.Resizable.positions;
27915     for(var i = 0, len = hs.length; i < len; i++){
27916         if(hs[i] && ps[hs[i]]){
27917             var pos = ps[hs[i]];
27918             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27919         }
27920     }
27921     // legacy
27922     this.corner = this.southeast;
27923     
27924     // updateBox = the box can move..
27925     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27926         this.updateBox = true;
27927     }
27928
27929     this.activeHandle = null;
27930
27931     if(this.resizeChild){
27932         if(typeof this.resizeChild == "boolean"){
27933             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27934         }else{
27935             this.resizeChild = Roo.get(this.resizeChild, true);
27936         }
27937     }
27938     
27939     if(this.adjustments == "auto"){
27940         var rc = this.resizeChild;
27941         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27942         if(rc && (hw || hn)){
27943             rc.position("relative");
27944             rc.setLeft(hw ? hw.el.getWidth() : 0);
27945             rc.setTop(hn ? hn.el.getHeight() : 0);
27946         }
27947         this.adjustments = [
27948             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27949             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27950         ];
27951     }
27952
27953     if(this.draggable){
27954         this.dd = this.dynamic ?
27955             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27956         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27957     }
27958
27959     // public events
27960     this.addEvents({
27961         /**
27962          * @event beforeresize
27963          * Fired before resize is allowed. Set enabled to false to cancel resize.
27964          * @param {Roo.Resizable} this
27965          * @param {Roo.EventObject} e The mousedown event
27966          */
27967         "beforeresize" : true,
27968         /**
27969          * @event resize
27970          * Fired after a resize.
27971          * @param {Roo.Resizable} this
27972          * @param {Number} width The new width
27973          * @param {Number} height The new height
27974          * @param {Roo.EventObject} e The mouseup event
27975          */
27976         "resize" : true
27977     });
27978
27979     if(this.width !== null && this.height !== null){
27980         this.resizeTo(this.width, this.height);
27981     }else{
27982         this.updateChildSize();
27983     }
27984     if(Roo.isIE){
27985         this.el.dom.style.zoom = 1;
27986     }
27987     Roo.Resizable.superclass.constructor.call(this);
27988 };
27989
27990 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27991         resizeChild : false,
27992         adjustments : [0, 0],
27993         minWidth : 5,
27994         minHeight : 5,
27995         maxWidth : 10000,
27996         maxHeight : 10000,
27997         enabled : true,
27998         animate : false,
27999         duration : .35,
28000         dynamic : false,
28001         handles : false,
28002         multiDirectional : false,
28003         disableTrackOver : false,
28004         easing : 'easeOutStrong',
28005         widthIncrement : 0,
28006         heightIncrement : 0,
28007         pinned : false,
28008         width : null,
28009         height : null,
28010         preserveRatio : false,
28011         transparent: false,
28012         minX: 0,
28013         minY: 0,
28014         draggable: false,
28015
28016         /**
28017          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28018          */
28019         constrainTo: undefined,
28020         /**
28021          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28022          */
28023         resizeRegion: undefined,
28024
28025
28026     /**
28027      * Perform a manual resize
28028      * @param {Number} width
28029      * @param {Number} height
28030      */
28031     resizeTo : function(width, height){
28032         this.el.setSize(width, height);
28033         this.updateChildSize();
28034         this.fireEvent("resize", this, width, height, null);
28035     },
28036
28037     // private
28038     startSizing : function(e, handle){
28039         this.fireEvent("beforeresize", this, e);
28040         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28041
28042             if(!this.overlay){
28043                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28044                 this.overlay.unselectable();
28045                 this.overlay.enableDisplayMode("block");
28046                 this.overlay.on("mousemove", this.onMouseMove, this);
28047                 this.overlay.on("mouseup", this.onMouseUp, this);
28048             }
28049             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28050
28051             this.resizing = true;
28052             this.startBox = this.el.getBox();
28053             this.startPoint = e.getXY();
28054             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28055                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28056
28057             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28058             this.overlay.show();
28059
28060             if(this.constrainTo) {
28061                 var ct = Roo.get(this.constrainTo);
28062                 this.resizeRegion = ct.getRegion().adjust(
28063                     ct.getFrameWidth('t'),
28064                     ct.getFrameWidth('l'),
28065                     -ct.getFrameWidth('b'),
28066                     -ct.getFrameWidth('r')
28067                 );
28068             }
28069
28070             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28071             this.proxy.show();
28072             this.proxy.setBox(this.startBox);
28073             if(!this.dynamic){
28074                 this.proxy.setStyle('visibility', 'visible');
28075             }
28076         }
28077     },
28078
28079     // private
28080     onMouseDown : function(handle, e){
28081         if(this.enabled){
28082             e.stopEvent();
28083             this.activeHandle = handle;
28084             this.startSizing(e, handle);
28085         }
28086     },
28087
28088     // private
28089     onMouseUp : function(e){
28090         var size = this.resizeElement();
28091         this.resizing = false;
28092         this.handleOut();
28093         this.overlay.hide();
28094         this.proxy.hide();
28095         this.fireEvent("resize", this, size.width, size.height, e);
28096     },
28097
28098     // private
28099     updateChildSize : function(){
28100         if(this.resizeChild){
28101             var el = this.el;
28102             var child = this.resizeChild;
28103             var adj = this.adjustments;
28104             if(el.dom.offsetWidth){
28105                 var b = el.getSize(true);
28106                 child.setSize(b.width+adj[0], b.height+adj[1]);
28107             }
28108             // Second call here for IE
28109             // The first call enables instant resizing and
28110             // the second call corrects scroll bars if they
28111             // exist
28112             if(Roo.isIE){
28113                 setTimeout(function(){
28114                     if(el.dom.offsetWidth){
28115                         var b = el.getSize(true);
28116                         child.setSize(b.width+adj[0], b.height+adj[1]);
28117                     }
28118                 }, 10);
28119             }
28120         }
28121     },
28122
28123     // private
28124     snap : function(value, inc, min){
28125         if(!inc || !value) return value;
28126         var newValue = value;
28127         var m = value % inc;
28128         if(m > 0){
28129             if(m > (inc/2)){
28130                 newValue = value + (inc-m);
28131             }else{
28132                 newValue = value - m;
28133             }
28134         }
28135         return Math.max(min, newValue);
28136     },
28137
28138     // private
28139     resizeElement : function(){
28140         var box = this.proxy.getBox();
28141         if(this.updateBox){
28142             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28143         }else{
28144             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28145         }
28146         this.updateChildSize();
28147         if(!this.dynamic){
28148             this.proxy.hide();
28149         }
28150         return box;
28151     },
28152
28153     // private
28154     constrain : function(v, diff, m, mx){
28155         if(v - diff < m){
28156             diff = v - m;
28157         }else if(v - diff > mx){
28158             diff = mx - v;
28159         }
28160         return diff;
28161     },
28162
28163     // private
28164     onMouseMove : function(e){
28165         if(this.enabled){
28166             try{// try catch so if something goes wrong the user doesn't get hung
28167
28168             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28169                 return;
28170             }
28171
28172             //var curXY = this.startPoint;
28173             var curSize = this.curSize || this.startBox;
28174             var x = this.startBox.x, y = this.startBox.y;
28175             var ox = x, oy = y;
28176             var w = curSize.width, h = curSize.height;
28177             var ow = w, oh = h;
28178             var mw = this.minWidth, mh = this.minHeight;
28179             var mxw = this.maxWidth, mxh = this.maxHeight;
28180             var wi = this.widthIncrement;
28181             var hi = this.heightIncrement;
28182
28183             var eventXY = e.getXY();
28184             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28185             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28186
28187             var pos = this.activeHandle.position;
28188
28189             switch(pos){
28190                 case "east":
28191                     w += diffX;
28192                     w = Math.min(Math.max(mw, w), mxw);
28193                     break;
28194              
28195                 case "south":
28196                     h += diffY;
28197                     h = Math.min(Math.max(mh, h), mxh);
28198                     break;
28199                 case "southeast":
28200                     w += diffX;
28201                     h += diffY;
28202                     w = Math.min(Math.max(mw, w), mxw);
28203                     h = Math.min(Math.max(mh, h), mxh);
28204                     break;
28205                 case "north":
28206                     diffY = this.constrain(h, diffY, mh, mxh);
28207                     y += diffY;
28208                     h -= diffY;
28209                     break;
28210                 case "hdrag":
28211                     
28212                     if (wi) {
28213                         var adiffX = Math.abs(diffX);
28214                         var sub = (adiffX % wi); // how much 
28215                         if (sub > (wi/2)) { // far enough to snap
28216                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28217                         } else {
28218                             // remove difference.. 
28219                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28220                         }
28221                     }
28222                     x += diffX;
28223                     x = Math.max(this.minX, x);
28224                     break;
28225                 case "west":
28226                     diffX = this.constrain(w, diffX, mw, mxw);
28227                     x += diffX;
28228                     w -= diffX;
28229                     break;
28230                 case "northeast":
28231                     w += diffX;
28232                     w = Math.min(Math.max(mw, w), mxw);
28233                     diffY = this.constrain(h, diffY, mh, mxh);
28234                     y += diffY;
28235                     h -= diffY;
28236                     break;
28237                 case "northwest":
28238                     diffX = this.constrain(w, diffX, mw, mxw);
28239                     diffY = this.constrain(h, diffY, mh, mxh);
28240                     y += diffY;
28241                     h -= diffY;
28242                     x += diffX;
28243                     w -= diffX;
28244                     break;
28245                case "southwest":
28246                     diffX = this.constrain(w, diffX, mw, mxw);
28247                     h += diffY;
28248                     h = Math.min(Math.max(mh, h), mxh);
28249                     x += diffX;
28250                     w -= diffX;
28251                     break;
28252             }
28253
28254             var sw = this.snap(w, wi, mw);
28255             var sh = this.snap(h, hi, mh);
28256             if(sw != w || sh != h){
28257                 switch(pos){
28258                     case "northeast":
28259                         y -= sh - h;
28260                     break;
28261                     case "north":
28262                         y -= sh - h;
28263                         break;
28264                     case "southwest":
28265                         x -= sw - w;
28266                     break;
28267                     case "west":
28268                         x -= sw - w;
28269                         break;
28270                     case "northwest":
28271                         x -= sw - w;
28272                         y -= sh - h;
28273                     break;
28274                 }
28275                 w = sw;
28276                 h = sh;
28277             }
28278
28279             if(this.preserveRatio){
28280                 switch(pos){
28281                     case "southeast":
28282                     case "east":
28283                         h = oh * (w/ow);
28284                         h = Math.min(Math.max(mh, h), mxh);
28285                         w = ow * (h/oh);
28286                        break;
28287                     case "south":
28288                         w = ow * (h/oh);
28289                         w = Math.min(Math.max(mw, w), mxw);
28290                         h = oh * (w/ow);
28291                         break;
28292                     case "northeast":
28293                         w = ow * (h/oh);
28294                         w = Math.min(Math.max(mw, w), mxw);
28295                         h = oh * (w/ow);
28296                     break;
28297                     case "north":
28298                         var tw = w;
28299                         w = ow * (h/oh);
28300                         w = Math.min(Math.max(mw, w), mxw);
28301                         h = oh * (w/ow);
28302                         x += (tw - w) / 2;
28303                         break;
28304                     case "southwest":
28305                         h = oh * (w/ow);
28306                         h = Math.min(Math.max(mh, h), mxh);
28307                         var tw = w;
28308                         w = ow * (h/oh);
28309                         x += tw - w;
28310                         break;
28311                     case "west":
28312                         var th = h;
28313                         h = oh * (w/ow);
28314                         h = Math.min(Math.max(mh, h), mxh);
28315                         y += (th - h) / 2;
28316                         var tw = w;
28317                         w = ow * (h/oh);
28318                         x += tw - w;
28319                        break;
28320                     case "northwest":
28321                         var tw = w;
28322                         var th = h;
28323                         h = oh * (w/ow);
28324                         h = Math.min(Math.max(mh, h), mxh);
28325                         w = ow * (h/oh);
28326                         y += th - h;
28327                         x += tw - w;
28328                        break;
28329
28330                 }
28331             }
28332             if (pos == 'hdrag') {
28333                 w = ow;
28334             }
28335             this.proxy.setBounds(x, y, w, h);
28336             if(this.dynamic){
28337                 this.resizeElement();
28338             }
28339             }catch(e){}
28340         }
28341     },
28342
28343     // private
28344     handleOver : function(){
28345         if(this.enabled){
28346             this.el.addClass("x-resizable-over");
28347         }
28348     },
28349
28350     // private
28351     handleOut : function(){
28352         if(!this.resizing){
28353             this.el.removeClass("x-resizable-over");
28354         }
28355     },
28356
28357     /**
28358      * Returns the element this component is bound to.
28359      * @return {Roo.Element}
28360      */
28361     getEl : function(){
28362         return this.el;
28363     },
28364
28365     /**
28366      * Returns the resizeChild element (or null).
28367      * @return {Roo.Element}
28368      */
28369     getResizeChild : function(){
28370         return this.resizeChild;
28371     },
28372
28373     /**
28374      * Destroys this resizable. If the element was wrapped and
28375      * removeEl is not true then the element remains.
28376      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28377      */
28378     destroy : function(removeEl){
28379         this.proxy.remove();
28380         if(this.overlay){
28381             this.overlay.removeAllListeners();
28382             this.overlay.remove();
28383         }
28384         var ps = Roo.Resizable.positions;
28385         for(var k in ps){
28386             if(typeof ps[k] != "function" && this[ps[k]]){
28387                 var h = this[ps[k]];
28388                 h.el.removeAllListeners();
28389                 h.el.remove();
28390             }
28391         }
28392         if(removeEl){
28393             this.el.update("");
28394             this.el.remove();
28395         }
28396     }
28397 });
28398
28399 // private
28400 // hash to map config positions to true positions
28401 Roo.Resizable.positions = {
28402     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28403     hd: "hdrag"
28404 };
28405
28406 // private
28407 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28408     if(!this.tpl){
28409         // only initialize the template if resizable is used
28410         var tpl = Roo.DomHelper.createTemplate(
28411             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28412         );
28413         tpl.compile();
28414         Roo.Resizable.Handle.prototype.tpl = tpl;
28415     }
28416     this.position = pos;
28417     this.rz = rz;
28418     // show north drag fro topdra
28419     var handlepos = pos == 'hdrag' ? 'north' : pos;
28420     
28421     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28422     if (pos == 'hdrag') {
28423         this.el.setStyle('cursor', 'pointer');
28424     }
28425     this.el.unselectable();
28426     if(transparent){
28427         this.el.setOpacity(0);
28428     }
28429     this.el.on("mousedown", this.onMouseDown, this);
28430     if(!disableTrackOver){
28431         this.el.on("mouseover", this.onMouseOver, this);
28432         this.el.on("mouseout", this.onMouseOut, this);
28433     }
28434 };
28435
28436 // private
28437 Roo.Resizable.Handle.prototype = {
28438     afterResize : function(rz){
28439         // do nothing
28440     },
28441     // private
28442     onMouseDown : function(e){
28443         this.rz.onMouseDown(this, e);
28444     },
28445     // private
28446     onMouseOver : function(e){
28447         this.rz.handleOver(this, e);
28448     },
28449     // private
28450     onMouseOut : function(e){
28451         this.rz.handleOut(this, e);
28452     }
28453 };/*
28454  * Based on:
28455  * Ext JS Library 1.1.1
28456  * Copyright(c) 2006-2007, Ext JS, LLC.
28457  *
28458  * Originally Released Under LGPL - original licence link has changed is not relivant.
28459  *
28460  * Fork - LGPL
28461  * <script type="text/javascript">
28462  */
28463
28464 /**
28465  * @class Roo.Editor
28466  * @extends Roo.Component
28467  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28468  * @constructor
28469  * Create a new Editor
28470  * @param {Roo.form.Field} field The Field object (or descendant)
28471  * @param {Object} config The config object
28472  */
28473 Roo.Editor = function(field, config){
28474     Roo.Editor.superclass.constructor.call(this, config);
28475     this.field = field;
28476     this.addEvents({
28477         /**
28478              * @event beforestartedit
28479              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28480              * false from the handler of this event.
28481              * @param {Editor} this
28482              * @param {Roo.Element} boundEl The underlying element bound to this editor
28483              * @param {Mixed} value The field value being set
28484              */
28485         "beforestartedit" : true,
28486         /**
28487              * @event startedit
28488              * Fires when this editor is displayed
28489              * @param {Roo.Element} boundEl The underlying element bound to this editor
28490              * @param {Mixed} value The starting field value
28491              */
28492         "startedit" : true,
28493         /**
28494              * @event beforecomplete
28495              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28496              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28497              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28498              * event will not fire since no edit actually occurred.
28499              * @param {Editor} this
28500              * @param {Mixed} value The current field value
28501              * @param {Mixed} startValue The original field value
28502              */
28503         "beforecomplete" : true,
28504         /**
28505              * @event complete
28506              * Fires after editing is complete and any changed value has been written to the underlying field.
28507              * @param {Editor} this
28508              * @param {Mixed} value The current field value
28509              * @param {Mixed} startValue The original field value
28510              */
28511         "complete" : true,
28512         /**
28513          * @event specialkey
28514          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28515          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28516          * @param {Roo.form.Field} this
28517          * @param {Roo.EventObject} e The event object
28518          */
28519         "specialkey" : true
28520     });
28521 };
28522
28523 Roo.extend(Roo.Editor, Roo.Component, {
28524     /**
28525      * @cfg {Boolean/String} autosize
28526      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28527      * or "height" to adopt the height only (defaults to false)
28528      */
28529     /**
28530      * @cfg {Boolean} revertInvalid
28531      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28532      * validation fails (defaults to true)
28533      */
28534     /**
28535      * @cfg {Boolean} ignoreNoChange
28536      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28537      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28538      * will never be ignored.
28539      */
28540     /**
28541      * @cfg {Boolean} hideEl
28542      * False to keep the bound element visible while the editor is displayed (defaults to true)
28543      */
28544     /**
28545      * @cfg {Mixed} value
28546      * The data value of the underlying field (defaults to "")
28547      */
28548     value : "",
28549     /**
28550      * @cfg {String} alignment
28551      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28552      */
28553     alignment: "c-c?",
28554     /**
28555      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28556      * for bottom-right shadow (defaults to "frame")
28557      */
28558     shadow : "frame",
28559     /**
28560      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28561      */
28562     constrain : false,
28563     /**
28564      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28565      */
28566     completeOnEnter : false,
28567     /**
28568      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28569      */
28570     cancelOnEsc : false,
28571     /**
28572      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28573      */
28574     updateEl : false,
28575
28576     // private
28577     onRender : function(ct, position){
28578         this.el = new Roo.Layer({
28579             shadow: this.shadow,
28580             cls: "x-editor",
28581             parentEl : ct,
28582             shim : this.shim,
28583             shadowOffset:4,
28584             id: this.id,
28585             constrain: this.constrain
28586         });
28587         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28588         if(this.field.msgTarget != 'title'){
28589             this.field.msgTarget = 'qtip';
28590         }
28591         this.field.render(this.el);
28592         if(Roo.isGecko){
28593             this.field.el.dom.setAttribute('autocomplete', 'off');
28594         }
28595         this.field.on("specialkey", this.onSpecialKey, this);
28596         if(this.swallowKeys){
28597             this.field.el.swallowEvent(['keydown','keypress']);
28598         }
28599         this.field.show();
28600         this.field.on("blur", this.onBlur, this);
28601         if(this.field.grow){
28602             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28603         }
28604     },
28605
28606     onSpecialKey : function(field, e)
28607     {
28608         //Roo.log('editor onSpecialKey');
28609         if(this.completeOnEnter && e.getKey() == e.ENTER){
28610             e.stopEvent();
28611             this.completeEdit();
28612             return;
28613         }
28614         // do not fire special key otherwise it might hide close the editor...
28615         if(e.getKey() == e.ENTER){    
28616             return;
28617         }
28618         if(this.cancelOnEsc && e.getKey() == e.ESC){
28619             this.cancelEdit();
28620             return;
28621         } 
28622         this.fireEvent('specialkey', field, e);
28623     
28624     },
28625
28626     /**
28627      * Starts the editing process and shows the editor.
28628      * @param {String/HTMLElement/Element} el The element to edit
28629      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28630       * to the innerHTML of el.
28631      */
28632     startEdit : function(el, value){
28633         if(this.editing){
28634             this.completeEdit();
28635         }
28636         this.boundEl = Roo.get(el);
28637         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28638         if(!this.rendered){
28639             this.render(this.parentEl || document.body);
28640         }
28641         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28642             return;
28643         }
28644         this.startValue = v;
28645         this.field.setValue(v);
28646         if(this.autoSize){
28647             var sz = this.boundEl.getSize();
28648             switch(this.autoSize){
28649                 case "width":
28650                 this.setSize(sz.width,  "");
28651                 break;
28652                 case "height":
28653                 this.setSize("",  sz.height);
28654                 break;
28655                 default:
28656                 this.setSize(sz.width,  sz.height);
28657             }
28658         }
28659         this.el.alignTo(this.boundEl, this.alignment);
28660         this.editing = true;
28661         if(Roo.QuickTips){
28662             Roo.QuickTips.disable();
28663         }
28664         this.show();
28665     },
28666
28667     /**
28668      * Sets the height and width of this editor.
28669      * @param {Number} width The new width
28670      * @param {Number} height The new height
28671      */
28672     setSize : function(w, h){
28673         this.field.setSize(w, h);
28674         if(this.el){
28675             this.el.sync();
28676         }
28677     },
28678
28679     /**
28680      * Realigns the editor to the bound field based on the current alignment config value.
28681      */
28682     realign : function(){
28683         this.el.alignTo(this.boundEl, this.alignment);
28684     },
28685
28686     /**
28687      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28688      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28689      */
28690     completeEdit : function(remainVisible){
28691         if(!this.editing){
28692             return;
28693         }
28694         var v = this.getValue();
28695         if(this.revertInvalid !== false && !this.field.isValid()){
28696             v = this.startValue;
28697             this.cancelEdit(true);
28698         }
28699         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28700             this.editing = false;
28701             this.hide();
28702             return;
28703         }
28704         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28705             this.editing = false;
28706             if(this.updateEl && this.boundEl){
28707                 this.boundEl.update(v);
28708             }
28709             if(remainVisible !== true){
28710                 this.hide();
28711             }
28712             this.fireEvent("complete", this, v, this.startValue);
28713         }
28714     },
28715
28716     // private
28717     onShow : function(){
28718         this.el.show();
28719         if(this.hideEl !== false){
28720             this.boundEl.hide();
28721         }
28722         this.field.show();
28723         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28724             this.fixIEFocus = true;
28725             this.deferredFocus.defer(50, this);
28726         }else{
28727             this.field.focus();
28728         }
28729         this.fireEvent("startedit", this.boundEl, this.startValue);
28730     },
28731
28732     deferredFocus : function(){
28733         if(this.editing){
28734             this.field.focus();
28735         }
28736     },
28737
28738     /**
28739      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28740      * reverted to the original starting value.
28741      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28742      * cancel (defaults to false)
28743      */
28744     cancelEdit : function(remainVisible){
28745         if(this.editing){
28746             this.setValue(this.startValue);
28747             if(remainVisible !== true){
28748                 this.hide();
28749             }
28750         }
28751     },
28752
28753     // private
28754     onBlur : function(){
28755         if(this.allowBlur !== true && this.editing){
28756             this.completeEdit();
28757         }
28758     },
28759
28760     // private
28761     onHide : function(){
28762         if(this.editing){
28763             this.completeEdit();
28764             return;
28765         }
28766         this.field.blur();
28767         if(this.field.collapse){
28768             this.field.collapse();
28769         }
28770         this.el.hide();
28771         if(this.hideEl !== false){
28772             this.boundEl.show();
28773         }
28774         if(Roo.QuickTips){
28775             Roo.QuickTips.enable();
28776         }
28777     },
28778
28779     /**
28780      * Sets the data value of the editor
28781      * @param {Mixed} value Any valid value supported by the underlying field
28782      */
28783     setValue : function(v){
28784         this.field.setValue(v);
28785     },
28786
28787     /**
28788      * Gets the data value of the editor
28789      * @return {Mixed} The data value
28790      */
28791     getValue : function(){
28792         return this.field.getValue();
28793     }
28794 });/*
28795  * Based on:
28796  * Ext JS Library 1.1.1
28797  * Copyright(c) 2006-2007, Ext JS, LLC.
28798  *
28799  * Originally Released Under LGPL - original licence link has changed is not relivant.
28800  *
28801  * Fork - LGPL
28802  * <script type="text/javascript">
28803  */
28804  
28805 /**
28806  * @class Roo.BasicDialog
28807  * @extends Roo.util.Observable
28808  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28809  * <pre><code>
28810 var dlg = new Roo.BasicDialog("my-dlg", {
28811     height: 200,
28812     width: 300,
28813     minHeight: 100,
28814     minWidth: 150,
28815     modal: true,
28816     proxyDrag: true,
28817     shadow: true
28818 });
28819 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28820 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28821 dlg.addButton('Cancel', dlg.hide, dlg);
28822 dlg.show();
28823 </code></pre>
28824   <b>A Dialog should always be a direct child of the body element.</b>
28825  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28826  * @cfg {String} title Default text to display in the title bar (defaults to null)
28827  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28828  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28829  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28830  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28831  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28832  * (defaults to null with no animation)
28833  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28834  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28835  * property for valid values (defaults to 'all')
28836  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28837  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28838  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28839  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28840  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28841  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28842  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28843  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28844  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28845  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28846  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28847  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28848  * draggable = true (defaults to false)
28849  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28850  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28851  * shadow (defaults to false)
28852  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28853  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28854  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28855  * @cfg {Array} buttons Array of buttons
28856  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28857  * @constructor
28858  * Create a new BasicDialog.
28859  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28860  * @param {Object} config Configuration options
28861  */
28862 Roo.BasicDialog = function(el, config){
28863     this.el = Roo.get(el);
28864     var dh = Roo.DomHelper;
28865     if(!this.el && config && config.autoCreate){
28866         if(typeof config.autoCreate == "object"){
28867             if(!config.autoCreate.id){
28868                 config.autoCreate.id = el;
28869             }
28870             this.el = dh.append(document.body,
28871                         config.autoCreate, true);
28872         }else{
28873             this.el = dh.append(document.body,
28874                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28875         }
28876     }
28877     el = this.el;
28878     el.setDisplayed(true);
28879     el.hide = this.hideAction;
28880     this.id = el.id;
28881     el.addClass("x-dlg");
28882
28883     Roo.apply(this, config);
28884
28885     this.proxy = el.createProxy("x-dlg-proxy");
28886     this.proxy.hide = this.hideAction;
28887     this.proxy.setOpacity(.5);
28888     this.proxy.hide();
28889
28890     if(config.width){
28891         el.setWidth(config.width);
28892     }
28893     if(config.height){
28894         el.setHeight(config.height);
28895     }
28896     this.size = el.getSize();
28897     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28898         this.xy = [config.x,config.y];
28899     }else{
28900         this.xy = el.getCenterXY(true);
28901     }
28902     /** The header element @type Roo.Element */
28903     this.header = el.child("> .x-dlg-hd");
28904     /** The body element @type Roo.Element */
28905     this.body = el.child("> .x-dlg-bd");
28906     /** The footer element @type Roo.Element */
28907     this.footer = el.child("> .x-dlg-ft");
28908
28909     if(!this.header){
28910         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28911     }
28912     if(!this.body){
28913         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28914     }
28915
28916     this.header.unselectable();
28917     if(this.title){
28918         this.header.update(this.title);
28919     }
28920     // this element allows the dialog to be focused for keyboard event
28921     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28922     this.focusEl.swallowEvent("click", true);
28923
28924     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28925
28926     // wrap the body and footer for special rendering
28927     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28928     if(this.footer){
28929         this.bwrap.dom.appendChild(this.footer.dom);
28930     }
28931
28932     this.bg = this.el.createChild({
28933         tag: "div", cls:"x-dlg-bg",
28934         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28935     });
28936     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28937
28938
28939     if(this.autoScroll !== false && !this.autoTabs){
28940         this.body.setStyle("overflow", "auto");
28941     }
28942
28943     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28944
28945     if(this.closable !== false){
28946         this.el.addClass("x-dlg-closable");
28947         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28948         this.close.on("click", this.closeClick, this);
28949         this.close.addClassOnOver("x-dlg-close-over");
28950     }
28951     if(this.collapsible !== false){
28952         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28953         this.collapseBtn.on("click", this.collapseClick, this);
28954         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28955         this.header.on("dblclick", this.collapseClick, this);
28956     }
28957     if(this.resizable !== false){
28958         this.el.addClass("x-dlg-resizable");
28959         this.resizer = new Roo.Resizable(el, {
28960             minWidth: this.minWidth || 80,
28961             minHeight:this.minHeight || 80,
28962             handles: this.resizeHandles || "all",
28963             pinned: true
28964         });
28965         this.resizer.on("beforeresize", this.beforeResize, this);
28966         this.resizer.on("resize", this.onResize, this);
28967     }
28968     if(this.draggable !== false){
28969         el.addClass("x-dlg-draggable");
28970         if (!this.proxyDrag) {
28971             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28972         }
28973         else {
28974             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28975         }
28976         dd.setHandleElId(this.header.id);
28977         dd.endDrag = this.endMove.createDelegate(this);
28978         dd.startDrag = this.startMove.createDelegate(this);
28979         dd.onDrag = this.onDrag.createDelegate(this);
28980         dd.scroll = false;
28981         this.dd = dd;
28982     }
28983     if(this.modal){
28984         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28985         this.mask.enableDisplayMode("block");
28986         this.mask.hide();
28987         this.el.addClass("x-dlg-modal");
28988     }
28989     if(this.shadow){
28990         this.shadow = new Roo.Shadow({
28991             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28992             offset : this.shadowOffset
28993         });
28994     }else{
28995         this.shadowOffset = 0;
28996     }
28997     if(Roo.useShims && this.shim !== false){
28998         this.shim = this.el.createShim();
28999         this.shim.hide = this.hideAction;
29000         this.shim.hide();
29001     }else{
29002         this.shim = false;
29003     }
29004     if(this.autoTabs){
29005         this.initTabs();
29006     }
29007     if (this.buttons) { 
29008         var bts= this.buttons;
29009         this.buttons = [];
29010         Roo.each(bts, function(b) {
29011             this.addButton(b);
29012         }, this);
29013     }
29014     
29015     
29016     this.addEvents({
29017         /**
29018          * @event keydown
29019          * Fires when a key is pressed
29020          * @param {Roo.BasicDialog} this
29021          * @param {Roo.EventObject} e
29022          */
29023         "keydown" : true,
29024         /**
29025          * @event move
29026          * Fires when this dialog is moved by the user.
29027          * @param {Roo.BasicDialog} this
29028          * @param {Number} x The new page X
29029          * @param {Number} y The new page Y
29030          */
29031         "move" : true,
29032         /**
29033          * @event resize
29034          * Fires when this dialog is resized by the user.
29035          * @param {Roo.BasicDialog} this
29036          * @param {Number} width The new width
29037          * @param {Number} height The new height
29038          */
29039         "resize" : true,
29040         /**
29041          * @event beforehide
29042          * Fires before this dialog is hidden.
29043          * @param {Roo.BasicDialog} this
29044          */
29045         "beforehide" : true,
29046         /**
29047          * @event hide
29048          * Fires when this dialog is hidden.
29049          * @param {Roo.BasicDialog} this
29050          */
29051         "hide" : true,
29052         /**
29053          * @event beforeshow
29054          * Fires before this dialog is shown.
29055          * @param {Roo.BasicDialog} this
29056          */
29057         "beforeshow" : true,
29058         /**
29059          * @event show
29060          * Fires when this dialog is shown.
29061          * @param {Roo.BasicDialog} this
29062          */
29063         "show" : true
29064     });
29065     el.on("keydown", this.onKeyDown, this);
29066     el.on("mousedown", this.toFront, this);
29067     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29068     this.el.hide();
29069     Roo.DialogManager.register(this);
29070     Roo.BasicDialog.superclass.constructor.call(this);
29071 };
29072
29073 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29074     shadowOffset: Roo.isIE ? 6 : 5,
29075     minHeight: 80,
29076     minWidth: 200,
29077     minButtonWidth: 75,
29078     defaultButton: null,
29079     buttonAlign: "right",
29080     tabTag: 'div',
29081     firstShow: true,
29082
29083     /**
29084      * Sets the dialog title text
29085      * @param {String} text The title text to display
29086      * @return {Roo.BasicDialog} this
29087      */
29088     setTitle : function(text){
29089         this.header.update(text);
29090         return this;
29091     },
29092
29093     // private
29094     closeClick : function(){
29095         this.hide();
29096     },
29097
29098     // private
29099     collapseClick : function(){
29100         this[this.collapsed ? "expand" : "collapse"]();
29101     },
29102
29103     /**
29104      * Collapses the dialog to its minimized state (only the title bar is visible).
29105      * Equivalent to the user clicking the collapse dialog button.
29106      */
29107     collapse : function(){
29108         if(!this.collapsed){
29109             this.collapsed = true;
29110             this.el.addClass("x-dlg-collapsed");
29111             this.restoreHeight = this.el.getHeight();
29112             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29113         }
29114     },
29115
29116     /**
29117      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29118      * clicking the expand dialog button.
29119      */
29120     expand : function(){
29121         if(this.collapsed){
29122             this.collapsed = false;
29123             this.el.removeClass("x-dlg-collapsed");
29124             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29125         }
29126     },
29127
29128     /**
29129      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29130      * @return {Roo.TabPanel} The tabs component
29131      */
29132     initTabs : function(){
29133         var tabs = this.getTabs();
29134         while(tabs.getTab(0)){
29135             tabs.removeTab(0);
29136         }
29137         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29138             var dom = el.dom;
29139             tabs.addTab(Roo.id(dom), dom.title);
29140             dom.title = "";
29141         });
29142         tabs.activate(0);
29143         return tabs;
29144     },
29145
29146     // private
29147     beforeResize : function(){
29148         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29149     },
29150
29151     // private
29152     onResize : function(){
29153         this.refreshSize();
29154         this.syncBodyHeight();
29155         this.adjustAssets();
29156         this.focus();
29157         this.fireEvent("resize", this, this.size.width, this.size.height);
29158     },
29159
29160     // private
29161     onKeyDown : function(e){
29162         if(this.isVisible()){
29163             this.fireEvent("keydown", this, e);
29164         }
29165     },
29166
29167     /**
29168      * Resizes the dialog.
29169      * @param {Number} width
29170      * @param {Number} height
29171      * @return {Roo.BasicDialog} this
29172      */
29173     resizeTo : function(width, height){
29174         this.el.setSize(width, height);
29175         this.size = {width: width, height: height};
29176         this.syncBodyHeight();
29177         if(this.fixedcenter){
29178             this.center();
29179         }
29180         if(this.isVisible()){
29181             this.constrainXY();
29182             this.adjustAssets();
29183         }
29184         this.fireEvent("resize", this, width, height);
29185         return this;
29186     },
29187
29188
29189     /**
29190      * Resizes the dialog to fit the specified content size.
29191      * @param {Number} width
29192      * @param {Number} height
29193      * @return {Roo.BasicDialog} this
29194      */
29195     setContentSize : function(w, h){
29196         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29197         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29198         //if(!this.el.isBorderBox()){
29199             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29200             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29201         //}
29202         if(this.tabs){
29203             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29204             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29205         }
29206         this.resizeTo(w, h);
29207         return this;
29208     },
29209
29210     /**
29211      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29212      * executed in response to a particular key being pressed while the dialog is active.
29213      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29214      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29215      * @param {Function} fn The function to call
29216      * @param {Object} scope (optional) The scope of the function
29217      * @return {Roo.BasicDialog} this
29218      */
29219     addKeyListener : function(key, fn, scope){
29220         var keyCode, shift, ctrl, alt;
29221         if(typeof key == "object" && !(key instanceof Array)){
29222             keyCode = key["key"];
29223             shift = key["shift"];
29224             ctrl = key["ctrl"];
29225             alt = key["alt"];
29226         }else{
29227             keyCode = key;
29228         }
29229         var handler = function(dlg, e){
29230             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29231                 var k = e.getKey();
29232                 if(keyCode instanceof Array){
29233                     for(var i = 0, len = keyCode.length; i < len; i++){
29234                         if(keyCode[i] == k){
29235                           fn.call(scope || window, dlg, k, e);
29236                           return;
29237                         }
29238                     }
29239                 }else{
29240                     if(k == keyCode){
29241                         fn.call(scope || window, dlg, k, e);
29242                     }
29243                 }
29244             }
29245         };
29246         this.on("keydown", handler);
29247         return this;
29248     },
29249
29250     /**
29251      * Returns the TabPanel component (creates it if it doesn't exist).
29252      * Note: If you wish to simply check for the existence of tabs without creating them,
29253      * check for a null 'tabs' property.
29254      * @return {Roo.TabPanel} The tabs component
29255      */
29256     getTabs : function(){
29257         if(!this.tabs){
29258             this.el.addClass("x-dlg-auto-tabs");
29259             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29260             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29261         }
29262         return this.tabs;
29263     },
29264
29265     /**
29266      * Adds a button to the footer section of the dialog.
29267      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29268      * object or a valid Roo.DomHelper element config
29269      * @param {Function} handler The function called when the button is clicked
29270      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29271      * @return {Roo.Button} The new button
29272      */
29273     addButton : function(config, handler, scope){
29274         var dh = Roo.DomHelper;
29275         if(!this.footer){
29276             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29277         }
29278         if(!this.btnContainer){
29279             var tb = this.footer.createChild({
29280
29281                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29282                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29283             }, null, true);
29284             this.btnContainer = tb.firstChild.firstChild.firstChild;
29285         }
29286         var bconfig = {
29287             handler: handler,
29288             scope: scope,
29289             minWidth: this.minButtonWidth,
29290             hideParent:true
29291         };
29292         if(typeof config == "string"){
29293             bconfig.text = config;
29294         }else{
29295             if(config.tag){
29296                 bconfig.dhconfig = config;
29297             }else{
29298                 Roo.apply(bconfig, config);
29299             }
29300         }
29301         var fc = false;
29302         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29303             bconfig.position = Math.max(0, bconfig.position);
29304             fc = this.btnContainer.childNodes[bconfig.position];
29305         }
29306          
29307         var btn = new Roo.Button(
29308             fc ? 
29309                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29310                 : this.btnContainer.appendChild(document.createElement("td")),
29311             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29312             bconfig
29313         );
29314         this.syncBodyHeight();
29315         if(!this.buttons){
29316             /**
29317              * Array of all the buttons that have been added to this dialog via addButton
29318              * @type Array
29319              */
29320             this.buttons = [];
29321         }
29322         this.buttons.push(btn);
29323         return btn;
29324     },
29325
29326     /**
29327      * Sets the default button to be focused when the dialog is displayed.
29328      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29329      * @return {Roo.BasicDialog} this
29330      */
29331     setDefaultButton : function(btn){
29332         this.defaultButton = btn;
29333         return this;
29334     },
29335
29336     // private
29337     getHeaderFooterHeight : function(safe){
29338         var height = 0;
29339         if(this.header){
29340            height += this.header.getHeight();
29341         }
29342         if(this.footer){
29343            var fm = this.footer.getMargins();
29344             height += (this.footer.getHeight()+fm.top+fm.bottom);
29345         }
29346         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29347         height += this.centerBg.getPadding("tb");
29348         return height;
29349     },
29350
29351     // private
29352     syncBodyHeight : function(){
29353         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29354         var height = this.size.height - this.getHeaderFooterHeight(false);
29355         bd.setHeight(height-bd.getMargins("tb"));
29356         var hh = this.header.getHeight();
29357         var h = this.size.height-hh;
29358         cb.setHeight(h);
29359         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29360         bw.setHeight(h-cb.getPadding("tb"));
29361         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29362         bd.setWidth(bw.getWidth(true));
29363         if(this.tabs){
29364             this.tabs.syncHeight();
29365             if(Roo.isIE){
29366                 this.tabs.el.repaint();
29367             }
29368         }
29369     },
29370
29371     /**
29372      * Restores the previous state of the dialog if Roo.state is configured.
29373      * @return {Roo.BasicDialog} this
29374      */
29375     restoreState : function(){
29376         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29377         if(box && box.width){
29378             this.xy = [box.x, box.y];
29379             this.resizeTo(box.width, box.height);
29380         }
29381         return this;
29382     },
29383
29384     // private
29385     beforeShow : function(){
29386         this.expand();
29387         if(this.fixedcenter){
29388             this.xy = this.el.getCenterXY(true);
29389         }
29390         if(this.modal){
29391             Roo.get(document.body).addClass("x-body-masked");
29392             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29393             this.mask.show();
29394         }
29395         this.constrainXY();
29396     },
29397
29398     // private
29399     animShow : function(){
29400         var b = Roo.get(this.animateTarget).getBox();
29401         this.proxy.setSize(b.width, b.height);
29402         this.proxy.setLocation(b.x, b.y);
29403         this.proxy.show();
29404         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29405                     true, .35, this.showEl.createDelegate(this));
29406     },
29407
29408     /**
29409      * Shows the dialog.
29410      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29411      * @return {Roo.BasicDialog} this
29412      */
29413     show : function(animateTarget){
29414         if (this.fireEvent("beforeshow", this) === false){
29415             return;
29416         }
29417         if(this.syncHeightBeforeShow){
29418             this.syncBodyHeight();
29419         }else if(this.firstShow){
29420             this.firstShow = false;
29421             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29422         }
29423         this.animateTarget = animateTarget || this.animateTarget;
29424         if(!this.el.isVisible()){
29425             this.beforeShow();
29426             if(this.animateTarget && Roo.get(this.animateTarget)){
29427                 this.animShow();
29428             }else{
29429                 this.showEl();
29430             }
29431         }
29432         return this;
29433     },
29434
29435     // private
29436     showEl : function(){
29437         this.proxy.hide();
29438         this.el.setXY(this.xy);
29439         this.el.show();
29440         this.adjustAssets(true);
29441         this.toFront();
29442         this.focus();
29443         // IE peekaboo bug - fix found by Dave Fenwick
29444         if(Roo.isIE){
29445             this.el.repaint();
29446         }
29447         this.fireEvent("show", this);
29448     },
29449
29450     /**
29451      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29452      * dialog itself will receive focus.
29453      */
29454     focus : function(){
29455         if(this.defaultButton){
29456             this.defaultButton.focus();
29457         }else{
29458             this.focusEl.focus();
29459         }
29460     },
29461
29462     // private
29463     constrainXY : function(){
29464         if(this.constraintoviewport !== false){
29465             if(!this.viewSize){
29466                 if(this.container){
29467                     var s = this.container.getSize();
29468                     this.viewSize = [s.width, s.height];
29469                 }else{
29470                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29471                 }
29472             }
29473             var s = Roo.get(this.container||document).getScroll();
29474
29475             var x = this.xy[0], y = this.xy[1];
29476             var w = this.size.width, h = this.size.height;
29477             var vw = this.viewSize[0], vh = this.viewSize[1];
29478             // only move it if it needs it
29479             var moved = false;
29480             // first validate right/bottom
29481             if(x + w > vw+s.left){
29482                 x = vw - w;
29483                 moved = true;
29484             }
29485             if(y + h > vh+s.top){
29486                 y = vh - h;
29487                 moved = true;
29488             }
29489             // then make sure top/left isn't negative
29490             if(x < s.left){
29491                 x = s.left;
29492                 moved = true;
29493             }
29494             if(y < s.top){
29495                 y = s.top;
29496                 moved = true;
29497             }
29498             if(moved){
29499                 // cache xy
29500                 this.xy = [x, y];
29501                 if(this.isVisible()){
29502                     this.el.setLocation(x, y);
29503                     this.adjustAssets();
29504                 }
29505             }
29506         }
29507     },
29508
29509     // private
29510     onDrag : function(){
29511         if(!this.proxyDrag){
29512             this.xy = this.el.getXY();
29513             this.adjustAssets();
29514         }
29515     },
29516
29517     // private
29518     adjustAssets : function(doShow){
29519         var x = this.xy[0], y = this.xy[1];
29520         var w = this.size.width, h = this.size.height;
29521         if(doShow === true){
29522             if(this.shadow){
29523                 this.shadow.show(this.el);
29524             }
29525             if(this.shim){
29526                 this.shim.show();
29527             }
29528         }
29529         if(this.shadow && this.shadow.isVisible()){
29530             this.shadow.show(this.el);
29531         }
29532         if(this.shim && this.shim.isVisible()){
29533             this.shim.setBounds(x, y, w, h);
29534         }
29535     },
29536
29537     // private
29538     adjustViewport : function(w, h){
29539         if(!w || !h){
29540             w = Roo.lib.Dom.getViewWidth();
29541             h = Roo.lib.Dom.getViewHeight();
29542         }
29543         // cache the size
29544         this.viewSize = [w, h];
29545         if(this.modal && this.mask.isVisible()){
29546             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29547             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29548         }
29549         if(this.isVisible()){
29550             this.constrainXY();
29551         }
29552     },
29553
29554     /**
29555      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29556      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29557      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29558      */
29559     destroy : function(removeEl){
29560         if(this.isVisible()){
29561             this.animateTarget = null;
29562             this.hide();
29563         }
29564         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29565         if(this.tabs){
29566             this.tabs.destroy(removeEl);
29567         }
29568         Roo.destroy(
29569              this.shim,
29570              this.proxy,
29571              this.resizer,
29572              this.close,
29573              this.mask
29574         );
29575         if(this.dd){
29576             this.dd.unreg();
29577         }
29578         if(this.buttons){
29579            for(var i = 0, len = this.buttons.length; i < len; i++){
29580                this.buttons[i].destroy();
29581            }
29582         }
29583         this.el.removeAllListeners();
29584         if(removeEl === true){
29585             this.el.update("");
29586             this.el.remove();
29587         }
29588         Roo.DialogManager.unregister(this);
29589     },
29590
29591     // private
29592     startMove : function(){
29593         if(this.proxyDrag){
29594             this.proxy.show();
29595         }
29596         if(this.constraintoviewport !== false){
29597             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29598         }
29599     },
29600
29601     // private
29602     endMove : function(){
29603         if(!this.proxyDrag){
29604             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29605         }else{
29606             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29607             this.proxy.hide();
29608         }
29609         this.refreshSize();
29610         this.adjustAssets();
29611         this.focus();
29612         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29613     },
29614
29615     /**
29616      * Brings this dialog to the front of any other visible dialogs
29617      * @return {Roo.BasicDialog} this
29618      */
29619     toFront : function(){
29620         Roo.DialogManager.bringToFront(this);
29621         return this;
29622     },
29623
29624     /**
29625      * Sends this dialog to the back (under) of any other visible dialogs
29626      * @return {Roo.BasicDialog} this
29627      */
29628     toBack : function(){
29629         Roo.DialogManager.sendToBack(this);
29630         return this;
29631     },
29632
29633     /**
29634      * Centers this dialog in the viewport
29635      * @return {Roo.BasicDialog} this
29636      */
29637     center : function(){
29638         var xy = this.el.getCenterXY(true);
29639         this.moveTo(xy[0], xy[1]);
29640         return this;
29641     },
29642
29643     /**
29644      * Moves the dialog's top-left corner to the specified point
29645      * @param {Number} x
29646      * @param {Number} y
29647      * @return {Roo.BasicDialog} this
29648      */
29649     moveTo : function(x, y){
29650         this.xy = [x,y];
29651         if(this.isVisible()){
29652             this.el.setXY(this.xy);
29653             this.adjustAssets();
29654         }
29655         return this;
29656     },
29657
29658     /**
29659      * Aligns the dialog to the specified element
29660      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29661      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29662      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29663      * @return {Roo.BasicDialog} this
29664      */
29665     alignTo : function(element, position, offsets){
29666         this.xy = this.el.getAlignToXY(element, position, offsets);
29667         if(this.isVisible()){
29668             this.el.setXY(this.xy);
29669             this.adjustAssets();
29670         }
29671         return this;
29672     },
29673
29674     /**
29675      * Anchors an element to another element and realigns it when the window is resized.
29676      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29677      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29678      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29679      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29680      * is a number, it is used as the buffer delay (defaults to 50ms).
29681      * @return {Roo.BasicDialog} this
29682      */
29683     anchorTo : function(el, alignment, offsets, monitorScroll){
29684         var action = function(){
29685             this.alignTo(el, alignment, offsets);
29686         };
29687         Roo.EventManager.onWindowResize(action, this);
29688         var tm = typeof monitorScroll;
29689         if(tm != 'undefined'){
29690             Roo.EventManager.on(window, 'scroll', action, this,
29691                 {buffer: tm == 'number' ? monitorScroll : 50});
29692         }
29693         action.call(this);
29694         return this;
29695     },
29696
29697     /**
29698      * Returns true if the dialog is visible
29699      * @return {Boolean}
29700      */
29701     isVisible : function(){
29702         return this.el.isVisible();
29703     },
29704
29705     // private
29706     animHide : function(callback){
29707         var b = Roo.get(this.animateTarget).getBox();
29708         this.proxy.show();
29709         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29710         this.el.hide();
29711         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29712                     this.hideEl.createDelegate(this, [callback]));
29713     },
29714
29715     /**
29716      * Hides the dialog.
29717      * @param {Function} callback (optional) Function to call when the dialog is hidden
29718      * @return {Roo.BasicDialog} this
29719      */
29720     hide : function(callback){
29721         if (this.fireEvent("beforehide", this) === false){
29722             return;
29723         }
29724         if(this.shadow){
29725             this.shadow.hide();
29726         }
29727         if(this.shim) {
29728           this.shim.hide();
29729         }
29730         // sometimes animateTarget seems to get set.. causing problems...
29731         // this just double checks..
29732         if(this.animateTarget && Roo.get(this.animateTarget)) {
29733            this.animHide(callback);
29734         }else{
29735             this.el.hide();
29736             this.hideEl(callback);
29737         }
29738         return this;
29739     },
29740
29741     // private
29742     hideEl : function(callback){
29743         this.proxy.hide();
29744         if(this.modal){
29745             this.mask.hide();
29746             Roo.get(document.body).removeClass("x-body-masked");
29747         }
29748         this.fireEvent("hide", this);
29749         if(typeof callback == "function"){
29750             callback();
29751         }
29752     },
29753
29754     // private
29755     hideAction : function(){
29756         this.setLeft("-10000px");
29757         this.setTop("-10000px");
29758         this.setStyle("visibility", "hidden");
29759     },
29760
29761     // private
29762     refreshSize : function(){
29763         this.size = this.el.getSize();
29764         this.xy = this.el.getXY();
29765         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29766     },
29767
29768     // private
29769     // z-index is managed by the DialogManager and may be overwritten at any time
29770     setZIndex : function(index){
29771         if(this.modal){
29772             this.mask.setStyle("z-index", index);
29773         }
29774         if(this.shim){
29775             this.shim.setStyle("z-index", ++index);
29776         }
29777         if(this.shadow){
29778             this.shadow.setZIndex(++index);
29779         }
29780         this.el.setStyle("z-index", ++index);
29781         if(this.proxy){
29782             this.proxy.setStyle("z-index", ++index);
29783         }
29784         if(this.resizer){
29785             this.resizer.proxy.setStyle("z-index", ++index);
29786         }
29787
29788         this.lastZIndex = index;
29789     },
29790
29791     /**
29792      * Returns the element for this dialog
29793      * @return {Roo.Element} The underlying dialog Element
29794      */
29795     getEl : function(){
29796         return this.el;
29797     }
29798 });
29799
29800 /**
29801  * @class Roo.DialogManager
29802  * Provides global access to BasicDialogs that have been created and
29803  * support for z-indexing (layering) multiple open dialogs.
29804  */
29805 Roo.DialogManager = function(){
29806     var list = {};
29807     var accessList = [];
29808     var front = null;
29809
29810     // private
29811     var sortDialogs = function(d1, d2){
29812         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29813     };
29814
29815     // private
29816     var orderDialogs = function(){
29817         accessList.sort(sortDialogs);
29818         var seed = Roo.DialogManager.zseed;
29819         for(var i = 0, len = accessList.length; i < len; i++){
29820             var dlg = accessList[i];
29821             if(dlg){
29822                 dlg.setZIndex(seed + (i*10));
29823             }
29824         }
29825     };
29826
29827     return {
29828         /**
29829          * The starting z-index for BasicDialogs (defaults to 9000)
29830          * @type Number The z-index value
29831          */
29832         zseed : 9000,
29833
29834         // private
29835         register : function(dlg){
29836             list[dlg.id] = dlg;
29837             accessList.push(dlg);
29838         },
29839
29840         // private
29841         unregister : function(dlg){
29842             delete list[dlg.id];
29843             var i=0;
29844             var len=0;
29845             if(!accessList.indexOf){
29846                 for(  i = 0, len = accessList.length; i < len; i++){
29847                     if(accessList[i] == dlg){
29848                         accessList.splice(i, 1);
29849                         return;
29850                     }
29851                 }
29852             }else{
29853                  i = accessList.indexOf(dlg);
29854                 if(i != -1){
29855                     accessList.splice(i, 1);
29856                 }
29857             }
29858         },
29859
29860         /**
29861          * Gets a registered dialog by id
29862          * @param {String/Object} id The id of the dialog or a dialog
29863          * @return {Roo.BasicDialog} this
29864          */
29865         get : function(id){
29866             return typeof id == "object" ? id : list[id];
29867         },
29868
29869         /**
29870          * Brings the specified dialog to the front
29871          * @param {String/Object} dlg The id of the dialog or a dialog
29872          * @return {Roo.BasicDialog} this
29873          */
29874         bringToFront : function(dlg){
29875             dlg = this.get(dlg);
29876             if(dlg != front){
29877                 front = dlg;
29878                 dlg._lastAccess = new Date().getTime();
29879                 orderDialogs();
29880             }
29881             return dlg;
29882         },
29883
29884         /**
29885          * Sends the specified dialog to the back
29886          * @param {String/Object} dlg The id of the dialog or a dialog
29887          * @return {Roo.BasicDialog} this
29888          */
29889         sendToBack : function(dlg){
29890             dlg = this.get(dlg);
29891             dlg._lastAccess = -(new Date().getTime());
29892             orderDialogs();
29893             return dlg;
29894         },
29895
29896         /**
29897          * Hides all dialogs
29898          */
29899         hideAll : function(){
29900             for(var id in list){
29901                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29902                     list[id].hide();
29903                 }
29904             }
29905         }
29906     };
29907 }();
29908
29909 /**
29910  * @class Roo.LayoutDialog
29911  * @extends Roo.BasicDialog
29912  * Dialog which provides adjustments for working with a layout in a Dialog.
29913  * Add your necessary layout config options to the dialog's config.<br>
29914  * Example usage (including a nested layout):
29915  * <pre><code>
29916 if(!dialog){
29917     dialog = new Roo.LayoutDialog("download-dlg", {
29918         modal: true,
29919         width:600,
29920         height:450,
29921         shadow:true,
29922         minWidth:500,
29923         minHeight:350,
29924         autoTabs:true,
29925         proxyDrag:true,
29926         // layout config merges with the dialog config
29927         center:{
29928             tabPosition: "top",
29929             alwaysShowTabs: true
29930         }
29931     });
29932     dialog.addKeyListener(27, dialog.hide, dialog);
29933     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29934     dialog.addButton("Build It!", this.getDownload, this);
29935
29936     // we can even add nested layouts
29937     var innerLayout = new Roo.BorderLayout("dl-inner", {
29938         east: {
29939             initialSize: 200,
29940             autoScroll:true,
29941             split:true
29942         },
29943         center: {
29944             autoScroll:true
29945         }
29946     });
29947     innerLayout.beginUpdate();
29948     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29949     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29950     innerLayout.endUpdate(true);
29951
29952     var layout = dialog.getLayout();
29953     layout.beginUpdate();
29954     layout.add("center", new Roo.ContentPanel("standard-panel",
29955                         {title: "Download the Source", fitToFrame:true}));
29956     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29957                {title: "Build your own roo.js"}));
29958     layout.getRegion("center").showPanel(sp);
29959     layout.endUpdate();
29960 }
29961 </code></pre>
29962     * @constructor
29963     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29964     * @param {Object} config configuration options
29965   */
29966 Roo.LayoutDialog = function(el, cfg){
29967     
29968     var config=  cfg;
29969     if (typeof(cfg) == 'undefined') {
29970         config = Roo.apply({}, el);
29971         // not sure why we use documentElement here.. - it should always be body.
29972         // IE7 borks horribly if we use documentElement.
29973         // webkit also does not like documentElement - it creates a body element...
29974         el = Roo.get( document.body || document.documentElement ).createChild();
29975         //config.autoCreate = true;
29976     }
29977     
29978     
29979     config.autoTabs = false;
29980     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29981     this.body.setStyle({overflow:"hidden", position:"relative"});
29982     this.layout = new Roo.BorderLayout(this.body.dom, config);
29983     this.layout.monitorWindowResize = false;
29984     this.el.addClass("x-dlg-auto-layout");
29985     // fix case when center region overwrites center function
29986     this.center = Roo.BasicDialog.prototype.center;
29987     this.on("show", this.layout.layout, this.layout, true);
29988     if (config.items) {
29989         var xitems = config.items;
29990         delete config.items;
29991         Roo.each(xitems, this.addxtype, this);
29992     }
29993     
29994     
29995 };
29996 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29997     /**
29998      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29999      * @deprecated
30000      */
30001     endUpdate : function(){
30002         this.layout.endUpdate();
30003     },
30004
30005     /**
30006      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30007      *  @deprecated
30008      */
30009     beginUpdate : function(){
30010         this.layout.beginUpdate();
30011     },
30012
30013     /**
30014      * Get the BorderLayout for this dialog
30015      * @return {Roo.BorderLayout}
30016      */
30017     getLayout : function(){
30018         return this.layout;
30019     },
30020
30021     showEl : function(){
30022         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30023         if(Roo.isIE7){
30024             this.layout.layout();
30025         }
30026     },
30027
30028     // private
30029     // Use the syncHeightBeforeShow config option to control this automatically
30030     syncBodyHeight : function(){
30031         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30032         if(this.layout){this.layout.layout();}
30033     },
30034     
30035       /**
30036      * Add an xtype element (actually adds to the layout.)
30037      * @return {Object} xdata xtype object data.
30038      */
30039     
30040     addxtype : function(c) {
30041         return this.layout.addxtype(c);
30042     }
30043 });/*
30044  * Based on:
30045  * Ext JS Library 1.1.1
30046  * Copyright(c) 2006-2007, Ext JS, LLC.
30047  *
30048  * Originally Released Under LGPL - original licence link has changed is not relivant.
30049  *
30050  * Fork - LGPL
30051  * <script type="text/javascript">
30052  */
30053  
30054 /**
30055  * @class Roo.MessageBox
30056  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30057  * Example usage:
30058  *<pre><code>
30059 // Basic alert:
30060 Roo.Msg.alert('Status', 'Changes saved successfully.');
30061
30062 // Prompt for user data:
30063 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30064     if (btn == 'ok'){
30065         // process text value...
30066     }
30067 });
30068
30069 // Show a dialog using config options:
30070 Roo.Msg.show({
30071    title:'Save Changes?',
30072    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30073    buttons: Roo.Msg.YESNOCANCEL,
30074    fn: processResult,
30075    animEl: 'elId'
30076 });
30077 </code></pre>
30078  * @singleton
30079  */
30080 Roo.MessageBox = function(){
30081     var dlg, opt, mask, waitTimer;
30082     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30083     var buttons, activeTextEl, bwidth;
30084
30085     // private
30086     var handleButton = function(button){
30087         dlg.hide();
30088         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30089     };
30090
30091     // private
30092     var handleHide = function(){
30093         if(opt && opt.cls){
30094             dlg.el.removeClass(opt.cls);
30095         }
30096         if(waitTimer){
30097             Roo.TaskMgr.stop(waitTimer);
30098             waitTimer = null;
30099         }
30100     };
30101
30102     // private
30103     var updateButtons = function(b){
30104         var width = 0;
30105         if(!b){
30106             buttons["ok"].hide();
30107             buttons["cancel"].hide();
30108             buttons["yes"].hide();
30109             buttons["no"].hide();
30110             dlg.footer.dom.style.display = 'none';
30111             return width;
30112         }
30113         dlg.footer.dom.style.display = '';
30114         for(var k in buttons){
30115             if(typeof buttons[k] != "function"){
30116                 if(b[k]){
30117                     buttons[k].show();
30118                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30119                     width += buttons[k].el.getWidth()+15;
30120                 }else{
30121                     buttons[k].hide();
30122                 }
30123             }
30124         }
30125         return width;
30126     };
30127
30128     // private
30129     var handleEsc = function(d, k, e){
30130         if(opt && opt.closable !== false){
30131             dlg.hide();
30132         }
30133         if(e){
30134             e.stopEvent();
30135         }
30136     };
30137
30138     return {
30139         /**
30140          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30141          * @return {Roo.BasicDialog} The BasicDialog element
30142          */
30143         getDialog : function(){
30144            if(!dlg){
30145                 dlg = new Roo.BasicDialog("x-msg-box", {
30146                     autoCreate : true,
30147                     shadow: true,
30148                     draggable: true,
30149                     resizable:false,
30150                     constraintoviewport:false,
30151                     fixedcenter:true,
30152                     collapsible : false,
30153                     shim:true,
30154                     modal: true,
30155                     width:400, height:100,
30156                     buttonAlign:"center",
30157                     closeClick : function(){
30158                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30159                             handleButton("no");
30160                         }else{
30161                             handleButton("cancel");
30162                         }
30163                     }
30164                 });
30165                 dlg.on("hide", handleHide);
30166                 mask = dlg.mask;
30167                 dlg.addKeyListener(27, handleEsc);
30168                 buttons = {};
30169                 var bt = this.buttonText;
30170                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30171                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30172                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30173                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30174                 bodyEl = dlg.body.createChild({
30175
30176                     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>'
30177                 });
30178                 msgEl = bodyEl.dom.firstChild;
30179                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30180                 textboxEl.enableDisplayMode();
30181                 textboxEl.addKeyListener([10,13], function(){
30182                     if(dlg.isVisible() && opt && opt.buttons){
30183                         if(opt.buttons.ok){
30184                             handleButton("ok");
30185                         }else if(opt.buttons.yes){
30186                             handleButton("yes");
30187                         }
30188                     }
30189                 });
30190                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30191                 textareaEl.enableDisplayMode();
30192                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30193                 progressEl.enableDisplayMode();
30194                 var pf = progressEl.dom.firstChild;
30195                 if (pf) {
30196                     pp = Roo.get(pf.firstChild);
30197                     pp.setHeight(pf.offsetHeight);
30198                 }
30199                 
30200             }
30201             return dlg;
30202         },
30203
30204         /**
30205          * Updates the message box body text
30206          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30207          * the XHTML-compliant non-breaking space character '&amp;#160;')
30208          * @return {Roo.MessageBox} This message box
30209          */
30210         updateText : function(text){
30211             if(!dlg.isVisible() && !opt.width){
30212                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30213             }
30214             msgEl.innerHTML = text || '&#160;';
30215       
30216             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30217             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30218             var w = Math.max(
30219                     Math.min(opt.width || cw , this.maxWidth), 
30220                     Math.max(opt.minWidth || this.minWidth, bwidth)
30221             );
30222             if(opt.prompt){
30223                 activeTextEl.setWidth(w);
30224             }
30225             if(dlg.isVisible()){
30226                 dlg.fixedcenter = false;
30227             }
30228             // to big, make it scroll. = But as usual stupid IE does not support
30229             // !important..
30230             
30231             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30232                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30233                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30234             } else {
30235                 bodyEl.dom.style.height = '';
30236                 bodyEl.dom.style.overflowY = '';
30237             }
30238             if (cw > w) {
30239                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30240             } else {
30241                 bodyEl.dom.style.overflowX = '';
30242             }
30243             
30244             dlg.setContentSize(w, bodyEl.getHeight());
30245             if(dlg.isVisible()){
30246                 dlg.fixedcenter = true;
30247             }
30248             return this;
30249         },
30250
30251         /**
30252          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30253          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30254          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30255          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30256          * @return {Roo.MessageBox} This message box
30257          */
30258         updateProgress : function(value, text){
30259             if(text){
30260                 this.updateText(text);
30261             }
30262             if (pp) { // weird bug on my firefox - for some reason this is not defined
30263                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30264             }
30265             return this;
30266         },        
30267
30268         /**
30269          * Returns true if the message box is currently displayed
30270          * @return {Boolean} True if the message box is visible, else false
30271          */
30272         isVisible : function(){
30273             return dlg && dlg.isVisible();  
30274         },
30275
30276         /**
30277          * Hides the message box if it is displayed
30278          */
30279         hide : function(){
30280             if(this.isVisible()){
30281                 dlg.hide();
30282             }  
30283         },
30284
30285         /**
30286          * Displays a new message box, or reinitializes an existing message box, based on the config options
30287          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30288          * The following config object properties are supported:
30289          * <pre>
30290 Property    Type             Description
30291 ----------  ---------------  ------------------------------------------------------------------------------------
30292 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30293                                    closes (defaults to undefined)
30294 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30295                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30296 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30297                                    progress and wait dialogs will ignore this property and always hide the
30298                                    close button as they can only be closed programmatically.
30299 cls               String           A custom CSS class to apply to the message box element
30300 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30301                                    displayed (defaults to 75)
30302 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30303                                    function will be btn (the name of the button that was clicked, if applicable,
30304                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30305                                    Progress and wait dialogs will ignore this option since they do not respond to
30306                                    user actions and can only be closed programmatically, so any required function
30307                                    should be called by the same code after it closes the dialog.
30308 icon              String           A CSS class that provides a background image to be used as an icon for
30309                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30310 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30311 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30312 modal             Boolean          False to allow user interaction with the page while the message box is
30313                                    displayed (defaults to true)
30314 msg               String           A string that will replace the existing message box body text (defaults
30315                                    to the XHTML-compliant non-breaking space character '&#160;')
30316 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30317 progress          Boolean          True to display a progress bar (defaults to false)
30318 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30319 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30320 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30321 title             String           The title text
30322 value             String           The string value to set into the active textbox element if displayed
30323 wait              Boolean          True to display a progress bar (defaults to false)
30324 width             Number           The width of the dialog in pixels
30325 </pre>
30326          *
30327          * Example usage:
30328          * <pre><code>
30329 Roo.Msg.show({
30330    title: 'Address',
30331    msg: 'Please enter your address:',
30332    width: 300,
30333    buttons: Roo.MessageBox.OKCANCEL,
30334    multiline: true,
30335    fn: saveAddress,
30336    animEl: 'addAddressBtn'
30337 });
30338 </code></pre>
30339          * @param {Object} config Configuration options
30340          * @return {Roo.MessageBox} This message box
30341          */
30342         show : function(options)
30343         {
30344             
30345             // this causes nightmares if you show one dialog after another
30346             // especially on callbacks..
30347              
30348             if(this.isVisible()){
30349                 
30350                 this.hide();
30351                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30352                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30353                 Roo.log("New Dialog Message:" +  options.msg )
30354                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30355                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30356                 
30357             }
30358             var d = this.getDialog();
30359             opt = options;
30360             d.setTitle(opt.title || "&#160;");
30361             d.close.setDisplayed(opt.closable !== false);
30362             activeTextEl = textboxEl;
30363             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30364             if(opt.prompt){
30365                 if(opt.multiline){
30366                     textboxEl.hide();
30367                     textareaEl.show();
30368                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30369                         opt.multiline : this.defaultTextHeight);
30370                     activeTextEl = textareaEl;
30371                 }else{
30372                     textboxEl.show();
30373                     textareaEl.hide();
30374                 }
30375             }else{
30376                 textboxEl.hide();
30377                 textareaEl.hide();
30378             }
30379             progressEl.setDisplayed(opt.progress === true);
30380             this.updateProgress(0);
30381             activeTextEl.dom.value = opt.value || "";
30382             if(opt.prompt){
30383                 dlg.setDefaultButton(activeTextEl);
30384             }else{
30385                 var bs = opt.buttons;
30386                 var db = null;
30387                 if(bs && bs.ok){
30388                     db = buttons["ok"];
30389                 }else if(bs && bs.yes){
30390                     db = buttons["yes"];
30391                 }
30392                 dlg.setDefaultButton(db);
30393             }
30394             bwidth = updateButtons(opt.buttons);
30395             this.updateText(opt.msg);
30396             if(opt.cls){
30397                 d.el.addClass(opt.cls);
30398             }
30399             d.proxyDrag = opt.proxyDrag === true;
30400             d.modal = opt.modal !== false;
30401             d.mask = opt.modal !== false ? mask : false;
30402             if(!d.isVisible()){
30403                 // force it to the end of the z-index stack so it gets a cursor in FF
30404                 document.body.appendChild(dlg.el.dom);
30405                 d.animateTarget = null;
30406                 d.show(options.animEl);
30407             }
30408             return this;
30409         },
30410
30411         /**
30412          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30413          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30414          * and closing the message box when the process is complete.
30415          * @param {String} title The title bar text
30416          * @param {String} msg The message box body text
30417          * @return {Roo.MessageBox} This message box
30418          */
30419         progress : function(title, msg){
30420             this.show({
30421                 title : title,
30422                 msg : msg,
30423                 buttons: false,
30424                 progress:true,
30425                 closable:false,
30426                 minWidth: this.minProgressWidth,
30427                 modal : true
30428             });
30429             return this;
30430         },
30431
30432         /**
30433          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30434          * If a callback function is passed it will be called after the user clicks the button, and the
30435          * id of the button that was clicked will be passed as the only parameter to the callback
30436          * (could also be the top-right close button).
30437          * @param {String} title The title bar text
30438          * @param {String} msg The message box body text
30439          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30440          * @param {Object} scope (optional) The scope of the callback function
30441          * @return {Roo.MessageBox} This message box
30442          */
30443         alert : function(title, msg, fn, scope){
30444             this.show({
30445                 title : title,
30446                 msg : msg,
30447                 buttons: this.OK,
30448                 fn: fn,
30449                 scope : scope,
30450                 modal : true
30451             });
30452             return this;
30453         },
30454
30455         /**
30456          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30457          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30458          * You are responsible for closing the message box when the process is complete.
30459          * @param {String} msg The message box body text
30460          * @param {String} title (optional) The title bar text
30461          * @return {Roo.MessageBox} This message box
30462          */
30463         wait : function(msg, title){
30464             this.show({
30465                 title : title,
30466                 msg : msg,
30467                 buttons: false,
30468                 closable:false,
30469                 progress:true,
30470                 modal:true,
30471                 width:300,
30472                 wait:true
30473             });
30474             waitTimer = Roo.TaskMgr.start({
30475                 run: function(i){
30476                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30477                 },
30478                 interval: 1000
30479             });
30480             return this;
30481         },
30482
30483         /**
30484          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30485          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30486          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30487          * @param {String} title The title bar text
30488          * @param {String} msg The message box body text
30489          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30490          * @param {Object} scope (optional) The scope of the callback function
30491          * @return {Roo.MessageBox} This message box
30492          */
30493         confirm : function(title, msg, fn, scope){
30494             this.show({
30495                 title : title,
30496                 msg : msg,
30497                 buttons: this.YESNO,
30498                 fn: fn,
30499                 scope : scope,
30500                 modal : true
30501             });
30502             return this;
30503         },
30504
30505         /**
30506          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30507          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30508          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30509          * (could also be the top-right close button) and the text that was entered will be passed as the two
30510          * parameters to the callback.
30511          * @param {String} title The title bar text
30512          * @param {String} msg The message box body text
30513          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30514          * @param {Object} scope (optional) The scope of the callback function
30515          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30516          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30517          * @return {Roo.MessageBox} This message box
30518          */
30519         prompt : function(title, msg, fn, scope, multiline){
30520             this.show({
30521                 title : title,
30522                 msg : msg,
30523                 buttons: this.OKCANCEL,
30524                 fn: fn,
30525                 minWidth:250,
30526                 scope : scope,
30527                 prompt:true,
30528                 multiline: multiline,
30529                 modal : true
30530             });
30531             return this;
30532         },
30533
30534         /**
30535          * Button config that displays a single OK button
30536          * @type Object
30537          */
30538         OK : {ok:true},
30539         /**
30540          * Button config that displays Yes and No buttons
30541          * @type Object
30542          */
30543         YESNO : {yes:true, no:true},
30544         /**
30545          * Button config that displays OK and Cancel buttons
30546          * @type Object
30547          */
30548         OKCANCEL : {ok:true, cancel:true},
30549         /**
30550          * Button config that displays Yes, No and Cancel buttons
30551          * @type Object
30552          */
30553         YESNOCANCEL : {yes:true, no:true, cancel:true},
30554
30555         /**
30556          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30557          * @type Number
30558          */
30559         defaultTextHeight : 75,
30560         /**
30561          * The maximum width in pixels of the message box (defaults to 600)
30562          * @type Number
30563          */
30564         maxWidth : 600,
30565         /**
30566          * The minimum width in pixels of the message box (defaults to 100)
30567          * @type Number
30568          */
30569         minWidth : 100,
30570         /**
30571          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30572          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30573          * @type Number
30574          */
30575         minProgressWidth : 250,
30576         /**
30577          * An object containing the default button text strings that can be overriden for localized language support.
30578          * Supported properties are: ok, cancel, yes and no.
30579          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30580          * @type Object
30581          */
30582         buttonText : {
30583             ok : "OK",
30584             cancel : "Cancel",
30585             yes : "Yes",
30586             no : "No"
30587         }
30588     };
30589 }();
30590
30591 /**
30592  * Shorthand for {@link Roo.MessageBox}
30593  */
30594 Roo.Msg = Roo.MessageBox;/*
30595  * Based on:
30596  * Ext JS Library 1.1.1
30597  * Copyright(c) 2006-2007, Ext JS, LLC.
30598  *
30599  * Originally Released Under LGPL - original licence link has changed is not relivant.
30600  *
30601  * Fork - LGPL
30602  * <script type="text/javascript">
30603  */
30604 /**
30605  * @class Roo.QuickTips
30606  * Provides attractive and customizable tooltips for any element.
30607  * @singleton
30608  */
30609 Roo.QuickTips = function(){
30610     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30611     var ce, bd, xy, dd;
30612     var visible = false, disabled = true, inited = false;
30613     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30614     
30615     var onOver = function(e){
30616         if(disabled){
30617             return;
30618         }
30619         var t = e.getTarget();
30620         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30621             return;
30622         }
30623         if(ce && t == ce.el){
30624             clearTimeout(hideProc);
30625             return;
30626         }
30627         if(t && tagEls[t.id]){
30628             tagEls[t.id].el = t;
30629             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30630             return;
30631         }
30632         var ttp, et = Roo.fly(t);
30633         var ns = cfg.namespace;
30634         if(tm.interceptTitles && t.title){
30635             ttp = t.title;
30636             t.qtip = ttp;
30637             t.removeAttribute("title");
30638             e.preventDefault();
30639         }else{
30640             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30641         }
30642         if(ttp){
30643             showProc = show.defer(tm.showDelay, tm, [{
30644                 el: t, 
30645                 text: ttp, 
30646                 width: et.getAttributeNS(ns, cfg.width),
30647                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30648                 title: et.getAttributeNS(ns, cfg.title),
30649                     cls: et.getAttributeNS(ns, cfg.cls)
30650             }]);
30651         }
30652     };
30653     
30654     var onOut = function(e){
30655         clearTimeout(showProc);
30656         var t = e.getTarget();
30657         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30658             hideProc = setTimeout(hide, tm.hideDelay);
30659         }
30660     };
30661     
30662     var onMove = function(e){
30663         if(disabled){
30664             return;
30665         }
30666         xy = e.getXY();
30667         xy[1] += 18;
30668         if(tm.trackMouse && ce){
30669             el.setXY(xy);
30670         }
30671     };
30672     
30673     var onDown = function(e){
30674         clearTimeout(showProc);
30675         clearTimeout(hideProc);
30676         if(!e.within(el)){
30677             if(tm.hideOnClick){
30678                 hide();
30679                 tm.disable();
30680                 tm.enable.defer(100, tm);
30681             }
30682         }
30683     };
30684     
30685     var getPad = function(){
30686         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30687     };
30688
30689     var show = function(o){
30690         if(disabled){
30691             return;
30692         }
30693         clearTimeout(dismissProc);
30694         ce = o;
30695         if(removeCls){ // in case manually hidden
30696             el.removeClass(removeCls);
30697             removeCls = null;
30698         }
30699         if(ce.cls){
30700             el.addClass(ce.cls);
30701             removeCls = ce.cls;
30702         }
30703         if(ce.title){
30704             tipTitle.update(ce.title);
30705             tipTitle.show();
30706         }else{
30707             tipTitle.update('');
30708             tipTitle.hide();
30709         }
30710         el.dom.style.width  = tm.maxWidth+'px';
30711         //tipBody.dom.style.width = '';
30712         tipBodyText.update(o.text);
30713         var p = getPad(), w = ce.width;
30714         if(!w){
30715             var td = tipBodyText.dom;
30716             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30717             if(aw > tm.maxWidth){
30718                 w = tm.maxWidth;
30719             }else if(aw < tm.minWidth){
30720                 w = tm.minWidth;
30721             }else{
30722                 w = aw;
30723             }
30724         }
30725         //tipBody.setWidth(w);
30726         el.setWidth(parseInt(w, 10) + p);
30727         if(ce.autoHide === false){
30728             close.setDisplayed(true);
30729             if(dd){
30730                 dd.unlock();
30731             }
30732         }else{
30733             close.setDisplayed(false);
30734             if(dd){
30735                 dd.lock();
30736             }
30737         }
30738         if(xy){
30739             el.avoidY = xy[1]-18;
30740             el.setXY(xy);
30741         }
30742         if(tm.animate){
30743             el.setOpacity(.1);
30744             el.setStyle("visibility", "visible");
30745             el.fadeIn({callback: afterShow});
30746         }else{
30747             afterShow();
30748         }
30749     };
30750     
30751     var afterShow = function(){
30752         if(ce){
30753             el.show();
30754             esc.enable();
30755             if(tm.autoDismiss && ce.autoHide !== false){
30756                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30757             }
30758         }
30759     };
30760     
30761     var hide = function(noanim){
30762         clearTimeout(dismissProc);
30763         clearTimeout(hideProc);
30764         ce = null;
30765         if(el.isVisible()){
30766             esc.disable();
30767             if(noanim !== true && tm.animate){
30768                 el.fadeOut({callback: afterHide});
30769             }else{
30770                 afterHide();
30771             } 
30772         }
30773     };
30774     
30775     var afterHide = function(){
30776         el.hide();
30777         if(removeCls){
30778             el.removeClass(removeCls);
30779             removeCls = null;
30780         }
30781     };
30782     
30783     return {
30784         /**
30785         * @cfg {Number} minWidth
30786         * The minimum width of the quick tip (defaults to 40)
30787         */
30788        minWidth : 40,
30789         /**
30790         * @cfg {Number} maxWidth
30791         * The maximum width of the quick tip (defaults to 300)
30792         */
30793        maxWidth : 300,
30794         /**
30795         * @cfg {Boolean} interceptTitles
30796         * True to automatically use the element's DOM title value if available (defaults to false)
30797         */
30798        interceptTitles : false,
30799         /**
30800         * @cfg {Boolean} trackMouse
30801         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30802         */
30803        trackMouse : false,
30804         /**
30805         * @cfg {Boolean} hideOnClick
30806         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30807         */
30808        hideOnClick : true,
30809         /**
30810         * @cfg {Number} showDelay
30811         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30812         */
30813        showDelay : 500,
30814         /**
30815         * @cfg {Number} hideDelay
30816         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30817         */
30818        hideDelay : 200,
30819         /**
30820         * @cfg {Boolean} autoHide
30821         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30822         * Used in conjunction with hideDelay.
30823         */
30824        autoHide : true,
30825         /**
30826         * @cfg {Boolean}
30827         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30828         * (defaults to true).  Used in conjunction with autoDismissDelay.
30829         */
30830        autoDismiss : true,
30831         /**
30832         * @cfg {Number}
30833         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30834         */
30835        autoDismissDelay : 5000,
30836        /**
30837         * @cfg {Boolean} animate
30838         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30839         */
30840        animate : false,
30841
30842        /**
30843         * @cfg {String} title
30844         * Title text to display (defaults to '').  This can be any valid HTML markup.
30845         */
30846         title: '',
30847        /**
30848         * @cfg {String} text
30849         * Body text to display (defaults to '').  This can be any valid HTML markup.
30850         */
30851         text : '',
30852        /**
30853         * @cfg {String} cls
30854         * A CSS class to apply to the base quick tip element (defaults to '').
30855         */
30856         cls : '',
30857        /**
30858         * @cfg {Number} width
30859         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30860         * minWidth or maxWidth.
30861         */
30862         width : null,
30863
30864     /**
30865      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30866      * or display QuickTips in a page.
30867      */
30868        init : function(){
30869           tm = Roo.QuickTips;
30870           cfg = tm.tagConfig;
30871           if(!inited){
30872               if(!Roo.isReady){ // allow calling of init() before onReady
30873                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30874                   return;
30875               }
30876               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30877               el.fxDefaults = {stopFx: true};
30878               // maximum custom styling
30879               //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>');
30880               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>');              
30881               tipTitle = el.child('h3');
30882               tipTitle.enableDisplayMode("block");
30883               tipBody = el.child('div.x-tip-bd');
30884               tipBodyText = el.child('div.x-tip-bd-inner');
30885               //bdLeft = el.child('div.x-tip-bd-left');
30886               //bdRight = el.child('div.x-tip-bd-right');
30887               close = el.child('div.x-tip-close');
30888               close.enableDisplayMode("block");
30889               close.on("click", hide);
30890               var d = Roo.get(document);
30891               d.on("mousedown", onDown);
30892               d.on("mouseover", onOver);
30893               d.on("mouseout", onOut);
30894               d.on("mousemove", onMove);
30895               esc = d.addKeyListener(27, hide);
30896               esc.disable();
30897               if(Roo.dd.DD){
30898                   dd = el.initDD("default", null, {
30899                       onDrag : function(){
30900                           el.sync();  
30901                       }
30902                   });
30903                   dd.setHandleElId(tipTitle.id);
30904                   dd.lock();
30905               }
30906               inited = true;
30907           }
30908           this.enable(); 
30909        },
30910
30911     /**
30912      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30913      * are supported:
30914      * <pre>
30915 Property    Type                   Description
30916 ----------  ---------------------  ------------------------------------------------------------------------
30917 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30918      * </ul>
30919      * @param {Object} config The config object
30920      */
30921        register : function(config){
30922            var cs = config instanceof Array ? config : arguments;
30923            for(var i = 0, len = cs.length; i < len; i++) {
30924                var c = cs[i];
30925                var target = c.target;
30926                if(target){
30927                    if(target instanceof Array){
30928                        for(var j = 0, jlen = target.length; j < jlen; j++){
30929                            tagEls[target[j]] = c;
30930                        }
30931                    }else{
30932                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30933                    }
30934                }
30935            }
30936        },
30937
30938     /**
30939      * Removes this quick tip from its element and destroys it.
30940      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30941      */
30942        unregister : function(el){
30943            delete tagEls[Roo.id(el)];
30944        },
30945
30946     /**
30947      * Enable this quick tip.
30948      */
30949        enable : function(){
30950            if(inited && disabled){
30951                locks.pop();
30952                if(locks.length < 1){
30953                    disabled = false;
30954                }
30955            }
30956        },
30957
30958     /**
30959      * Disable this quick tip.
30960      */
30961        disable : function(){
30962           disabled = true;
30963           clearTimeout(showProc);
30964           clearTimeout(hideProc);
30965           clearTimeout(dismissProc);
30966           if(ce){
30967               hide(true);
30968           }
30969           locks.push(1);
30970        },
30971
30972     /**
30973      * Returns true if the quick tip is enabled, else false.
30974      */
30975        isEnabled : function(){
30976             return !disabled;
30977        },
30978
30979         // private
30980        tagConfig : {
30981            namespace : "ext",
30982            attribute : "qtip",
30983            width : "width",
30984            target : "target",
30985            title : "qtitle",
30986            hide : "hide",
30987            cls : "qclass"
30988        }
30989    };
30990 }();
30991
30992 // backwards compat
30993 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30994  * Based on:
30995  * Ext JS Library 1.1.1
30996  * Copyright(c) 2006-2007, Ext JS, LLC.
30997  *
30998  * Originally Released Under LGPL - original licence link has changed is not relivant.
30999  *
31000  * Fork - LGPL
31001  * <script type="text/javascript">
31002  */
31003  
31004
31005 /**
31006  * @class Roo.tree.TreePanel
31007  * @extends Roo.data.Tree
31008
31009  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31010  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31011  * @cfg {Boolean} enableDD true to enable drag and drop
31012  * @cfg {Boolean} enableDrag true to enable just drag
31013  * @cfg {Boolean} enableDrop true to enable just drop
31014  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31015  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31016  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31017  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31018  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31019  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31020  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31021  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31022  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31023  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31024  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31025  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31026  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31027  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31028  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31029  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
31030  * 
31031  * @constructor
31032  * @param {String/HTMLElement/Element} el The container element
31033  * @param {Object} config
31034  */
31035 Roo.tree.TreePanel = function(el, config){
31036     var root = false;
31037     var loader = false;
31038     if (config.root) {
31039         root = config.root;
31040         delete config.root;
31041     }
31042     if (config.loader) {
31043         loader = config.loader;
31044         delete config.loader;
31045     }
31046     
31047     Roo.apply(this, config);
31048     Roo.tree.TreePanel.superclass.constructor.call(this);
31049     this.el = Roo.get(el);
31050     this.el.addClass('x-tree');
31051     //console.log(root);
31052     if (root) {
31053         this.setRootNode( Roo.factory(root, Roo.tree));
31054     }
31055     if (loader) {
31056         this.loader = Roo.factory(loader, Roo.tree);
31057     }
31058    /**
31059     * Read-only. The id of the container element becomes this TreePanel's id.
31060     */
31061     this.id = this.el.id;
31062     this.addEvents({
31063         /**
31064         * @event beforeload
31065         * Fires before a node is loaded, return false to cancel
31066         * @param {Node} node The node being loaded
31067         */
31068         "beforeload" : true,
31069         /**
31070         * @event load
31071         * Fires when a node is loaded
31072         * @param {Node} node The node that was loaded
31073         */
31074         "load" : true,
31075         /**
31076         * @event textchange
31077         * Fires when the text for a node is changed
31078         * @param {Node} node The node
31079         * @param {String} text The new text
31080         * @param {String} oldText The old text
31081         */
31082         "textchange" : true,
31083         /**
31084         * @event beforeexpand
31085         * Fires before a node is expanded, return false to cancel.
31086         * @param {Node} node The node
31087         * @param {Boolean} deep
31088         * @param {Boolean} anim
31089         */
31090         "beforeexpand" : true,
31091         /**
31092         * @event beforecollapse
31093         * Fires before a node is collapsed, return false to cancel.
31094         * @param {Node} node The node
31095         * @param {Boolean} deep
31096         * @param {Boolean} anim
31097         */
31098         "beforecollapse" : true,
31099         /**
31100         * @event expand
31101         * Fires when a node is expanded
31102         * @param {Node} node The node
31103         */
31104         "expand" : true,
31105         /**
31106         * @event disabledchange
31107         * Fires when the disabled status of a node changes
31108         * @param {Node} node The node
31109         * @param {Boolean} disabled
31110         */
31111         "disabledchange" : true,
31112         /**
31113         * @event collapse
31114         * Fires when a node is collapsed
31115         * @param {Node} node The node
31116         */
31117         "collapse" : true,
31118         /**
31119         * @event beforeclick
31120         * Fires before click processing on a node. Return false to cancel the default action.
31121         * @param {Node} node The node
31122         * @param {Roo.EventObject} e The event object
31123         */
31124         "beforeclick":true,
31125         /**
31126         * @event checkchange
31127         * Fires when a node with a checkbox's checked property changes
31128         * @param {Node} this This node
31129         * @param {Boolean} checked
31130         */
31131         "checkchange":true,
31132         /**
31133         * @event click
31134         * Fires when a node is clicked
31135         * @param {Node} node The node
31136         * @param {Roo.EventObject} e The event object
31137         */
31138         "click":true,
31139         /**
31140         * @event dblclick
31141         * Fires when a node is double clicked
31142         * @param {Node} node The node
31143         * @param {Roo.EventObject} e The event object
31144         */
31145         "dblclick":true,
31146         /**
31147         * @event contextmenu
31148         * Fires when a node is right clicked
31149         * @param {Node} node The node
31150         * @param {Roo.EventObject} e The event object
31151         */
31152         "contextmenu":true,
31153         /**
31154         * @event beforechildrenrendered
31155         * Fires right before the child nodes for a node are rendered
31156         * @param {Node} node The node
31157         */
31158         "beforechildrenrendered":true,
31159         /**
31160         * @event startdrag
31161         * Fires when a node starts being dragged
31162         * @param {Roo.tree.TreePanel} this
31163         * @param {Roo.tree.TreeNode} node
31164         * @param {event} e The raw browser event
31165         */ 
31166        "startdrag" : true,
31167        /**
31168         * @event enddrag
31169         * Fires when a drag operation is complete
31170         * @param {Roo.tree.TreePanel} this
31171         * @param {Roo.tree.TreeNode} node
31172         * @param {event} e The raw browser event
31173         */
31174        "enddrag" : true,
31175        /**
31176         * @event dragdrop
31177         * Fires when a dragged node is dropped on a valid DD target
31178         * @param {Roo.tree.TreePanel} this
31179         * @param {Roo.tree.TreeNode} node
31180         * @param {DD} dd The dd it was dropped on
31181         * @param {event} e The raw browser event
31182         */
31183        "dragdrop" : true,
31184        /**
31185         * @event beforenodedrop
31186         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31187         * passed to handlers has the following properties:<br />
31188         * <ul style="padding:5px;padding-left:16px;">
31189         * <li>tree - The TreePanel</li>
31190         * <li>target - The node being targeted for the drop</li>
31191         * <li>data - The drag data from the drag source</li>
31192         * <li>point - The point of the drop - append, above or below</li>
31193         * <li>source - The drag source</li>
31194         * <li>rawEvent - Raw mouse event</li>
31195         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31196         * to be inserted by setting them on this object.</li>
31197         * <li>cancel - Set this to true to cancel the drop.</li>
31198         * </ul>
31199         * @param {Object} dropEvent
31200         */
31201        "beforenodedrop" : true,
31202        /**
31203         * @event nodedrop
31204         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31205         * passed to handlers has the following properties:<br />
31206         * <ul style="padding:5px;padding-left:16px;">
31207         * <li>tree - The TreePanel</li>
31208         * <li>target - The node being targeted for the drop</li>
31209         * <li>data - The drag data from the drag source</li>
31210         * <li>point - The point of the drop - append, above or below</li>
31211         * <li>source - The drag source</li>
31212         * <li>rawEvent - Raw mouse event</li>
31213         * <li>dropNode - Dropped node(s).</li>
31214         * </ul>
31215         * @param {Object} dropEvent
31216         */
31217        "nodedrop" : true,
31218         /**
31219         * @event nodedragover
31220         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31221         * passed to handlers has the following properties:<br />
31222         * <ul style="padding:5px;padding-left:16px;">
31223         * <li>tree - The TreePanel</li>
31224         * <li>target - The node being targeted for the drop</li>
31225         * <li>data - The drag data from the drag source</li>
31226         * <li>point - The point of the drop - append, above or below</li>
31227         * <li>source - The drag source</li>
31228         * <li>rawEvent - Raw mouse event</li>
31229         * <li>dropNode - Drop node(s) provided by the source.</li>
31230         * <li>cancel - Set this to true to signal drop not allowed.</li>
31231         * </ul>
31232         * @param {Object} dragOverEvent
31233         */
31234        "nodedragover" : true
31235         
31236     });
31237     if(this.singleExpand){
31238        this.on("beforeexpand", this.restrictExpand, this);
31239     }
31240     if (this.editor) {
31241         this.editor.tree = this;
31242         this.editor = Roo.factory(this.editor, Roo.tree);
31243     }
31244     
31245     if (this.selModel) {
31246         this.selModel = Roo.factory(this.selModel, Roo.tree);
31247     }
31248    
31249 };
31250 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31251     rootVisible : true,
31252     animate: Roo.enableFx,
31253     lines : true,
31254     enableDD : false,
31255     hlDrop : Roo.enableFx,
31256   
31257     renderer: false,
31258     
31259     rendererTip: false,
31260     // private
31261     restrictExpand : function(node){
31262         var p = node.parentNode;
31263         if(p){
31264             if(p.expandedChild && p.expandedChild.parentNode == p){
31265                 p.expandedChild.collapse();
31266             }
31267             p.expandedChild = node;
31268         }
31269     },
31270
31271     // private override
31272     setRootNode : function(node){
31273         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31274         if(!this.rootVisible){
31275             node.ui = new Roo.tree.RootTreeNodeUI(node);
31276         }
31277         return node;
31278     },
31279
31280     /**
31281      * Returns the container element for this TreePanel
31282      */
31283     getEl : function(){
31284         return this.el;
31285     },
31286
31287     /**
31288      * Returns the default TreeLoader for this TreePanel
31289      */
31290     getLoader : function(){
31291         return this.loader;
31292     },
31293
31294     /**
31295      * Expand all nodes
31296      */
31297     expandAll : function(){
31298         this.root.expand(true);
31299     },
31300
31301     /**
31302      * Collapse all nodes
31303      */
31304     collapseAll : function(){
31305         this.root.collapse(true);
31306     },
31307
31308     /**
31309      * Returns the selection model used by this TreePanel
31310      */
31311     getSelectionModel : function(){
31312         if(!this.selModel){
31313             this.selModel = new Roo.tree.DefaultSelectionModel();
31314         }
31315         return this.selModel;
31316     },
31317
31318     /**
31319      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31320      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31321      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31322      * @return {Array}
31323      */
31324     getChecked : function(a, startNode){
31325         startNode = startNode || this.root;
31326         var r = [];
31327         var f = function(){
31328             if(this.attributes.checked){
31329                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31330             }
31331         }
31332         startNode.cascade(f);
31333         return r;
31334     },
31335
31336     /**
31337      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31338      * @param {String} path
31339      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31340      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31341      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31342      */
31343     expandPath : function(path, attr, callback){
31344         attr = attr || "id";
31345         var keys = path.split(this.pathSeparator);
31346         var curNode = this.root;
31347         if(curNode.attributes[attr] != keys[1]){ // invalid root
31348             if(callback){
31349                 callback(false, null);
31350             }
31351             return;
31352         }
31353         var index = 1;
31354         var f = function(){
31355             if(++index == keys.length){
31356                 if(callback){
31357                     callback(true, curNode);
31358                 }
31359                 return;
31360             }
31361             var c = curNode.findChild(attr, keys[index]);
31362             if(!c){
31363                 if(callback){
31364                     callback(false, curNode);
31365                 }
31366                 return;
31367             }
31368             curNode = c;
31369             c.expand(false, false, f);
31370         };
31371         curNode.expand(false, false, f);
31372     },
31373
31374     /**
31375      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31376      * @param {String} path
31377      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31378      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31379      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31380      */
31381     selectPath : function(path, attr, callback){
31382         attr = attr || "id";
31383         var keys = path.split(this.pathSeparator);
31384         var v = keys.pop();
31385         if(keys.length > 0){
31386             var f = function(success, node){
31387                 if(success && node){
31388                     var n = node.findChild(attr, v);
31389                     if(n){
31390                         n.select();
31391                         if(callback){
31392                             callback(true, n);
31393                         }
31394                     }else if(callback){
31395                         callback(false, n);
31396                     }
31397                 }else{
31398                     if(callback){
31399                         callback(false, n);
31400                     }
31401                 }
31402             };
31403             this.expandPath(keys.join(this.pathSeparator), attr, f);
31404         }else{
31405             this.root.select();
31406             if(callback){
31407                 callback(true, this.root);
31408             }
31409         }
31410     },
31411
31412     getTreeEl : function(){
31413         return this.el;
31414     },
31415
31416     /**
31417      * Trigger rendering of this TreePanel
31418      */
31419     render : function(){
31420         if (this.innerCt) {
31421             return this; // stop it rendering more than once!!
31422         }
31423         
31424         this.innerCt = this.el.createChild({tag:"ul",
31425                cls:"x-tree-root-ct " +
31426                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31427
31428         if(this.containerScroll){
31429             Roo.dd.ScrollManager.register(this.el);
31430         }
31431         if((this.enableDD || this.enableDrop) && !this.dropZone){
31432            /**
31433             * The dropZone used by this tree if drop is enabled
31434             * @type Roo.tree.TreeDropZone
31435             */
31436              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31437                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31438            });
31439         }
31440         if((this.enableDD || this.enableDrag) && !this.dragZone){
31441            /**
31442             * The dragZone used by this tree if drag is enabled
31443             * @type Roo.tree.TreeDragZone
31444             */
31445             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31446                ddGroup: this.ddGroup || "TreeDD",
31447                scroll: this.ddScroll
31448            });
31449         }
31450         this.getSelectionModel().init(this);
31451         if (!this.root) {
31452             Roo.log("ROOT not set in tree");
31453             return this;
31454         }
31455         this.root.render();
31456         if(!this.rootVisible){
31457             this.root.renderChildren();
31458         }
31459         return this;
31460     }
31461 });/*
31462  * Based on:
31463  * Ext JS Library 1.1.1
31464  * Copyright(c) 2006-2007, Ext JS, LLC.
31465  *
31466  * Originally Released Under LGPL - original licence link has changed is not relivant.
31467  *
31468  * Fork - LGPL
31469  * <script type="text/javascript">
31470  */
31471  
31472
31473 /**
31474  * @class Roo.tree.DefaultSelectionModel
31475  * @extends Roo.util.Observable
31476  * The default single selection for a TreePanel.
31477  * @param {Object} cfg Configuration
31478  */
31479 Roo.tree.DefaultSelectionModel = function(cfg){
31480    this.selNode = null;
31481    
31482    
31483    
31484    this.addEvents({
31485        /**
31486         * @event selectionchange
31487         * Fires when the selected node changes
31488         * @param {DefaultSelectionModel} this
31489         * @param {TreeNode} node the new selection
31490         */
31491        "selectionchange" : true,
31492
31493        /**
31494         * @event beforeselect
31495         * Fires before the selected node changes, return false to cancel the change
31496         * @param {DefaultSelectionModel} this
31497         * @param {TreeNode} node the new selection
31498         * @param {TreeNode} node the old selection
31499         */
31500        "beforeselect" : true
31501    });
31502    
31503     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31504 };
31505
31506 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31507     init : function(tree){
31508         this.tree = tree;
31509         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31510         tree.on("click", this.onNodeClick, this);
31511     },
31512     
31513     onNodeClick : function(node, e){
31514         if (e.ctrlKey && this.selNode == node)  {
31515             this.unselect(node);
31516             return;
31517         }
31518         this.select(node);
31519     },
31520     
31521     /**
31522      * Select a node.
31523      * @param {TreeNode} node The node to select
31524      * @return {TreeNode} The selected node
31525      */
31526     select : function(node){
31527         var last = this.selNode;
31528         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31529             if(last){
31530                 last.ui.onSelectedChange(false);
31531             }
31532             this.selNode = node;
31533             node.ui.onSelectedChange(true);
31534             this.fireEvent("selectionchange", this, node, last);
31535         }
31536         return node;
31537     },
31538     
31539     /**
31540      * Deselect a node.
31541      * @param {TreeNode} node The node to unselect
31542      */
31543     unselect : function(node){
31544         if(this.selNode == node){
31545             this.clearSelections();
31546         }    
31547     },
31548     
31549     /**
31550      * Clear all selections
31551      */
31552     clearSelections : function(){
31553         var n = this.selNode;
31554         if(n){
31555             n.ui.onSelectedChange(false);
31556             this.selNode = null;
31557             this.fireEvent("selectionchange", this, null);
31558         }
31559         return n;
31560     },
31561     
31562     /**
31563      * Get the selected node
31564      * @return {TreeNode} The selected node
31565      */
31566     getSelectedNode : function(){
31567         return this.selNode;    
31568     },
31569     
31570     /**
31571      * Returns true if the node is selected
31572      * @param {TreeNode} node The node to check
31573      * @return {Boolean}
31574      */
31575     isSelected : function(node){
31576         return this.selNode == node;  
31577     },
31578
31579     /**
31580      * Selects the node above the selected node in the tree, intelligently walking the nodes
31581      * @return TreeNode The new selection
31582      */
31583     selectPrevious : function(){
31584         var s = this.selNode || this.lastSelNode;
31585         if(!s){
31586             return null;
31587         }
31588         var ps = s.previousSibling;
31589         if(ps){
31590             if(!ps.isExpanded() || ps.childNodes.length < 1){
31591                 return this.select(ps);
31592             } else{
31593                 var lc = ps.lastChild;
31594                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31595                     lc = lc.lastChild;
31596                 }
31597                 return this.select(lc);
31598             }
31599         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31600             return this.select(s.parentNode);
31601         }
31602         return null;
31603     },
31604
31605     /**
31606      * Selects the node above the selected node in the tree, intelligently walking the nodes
31607      * @return TreeNode The new selection
31608      */
31609     selectNext : function(){
31610         var s = this.selNode || this.lastSelNode;
31611         if(!s){
31612             return null;
31613         }
31614         if(s.firstChild && s.isExpanded()){
31615              return this.select(s.firstChild);
31616          }else if(s.nextSibling){
31617              return this.select(s.nextSibling);
31618          }else if(s.parentNode){
31619             var newS = null;
31620             s.parentNode.bubble(function(){
31621                 if(this.nextSibling){
31622                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31623                     return false;
31624                 }
31625             });
31626             return newS;
31627          }
31628         return null;
31629     },
31630
31631     onKeyDown : function(e){
31632         var s = this.selNode || this.lastSelNode;
31633         // undesirable, but required
31634         var sm = this;
31635         if(!s){
31636             return;
31637         }
31638         var k = e.getKey();
31639         switch(k){
31640              case e.DOWN:
31641                  e.stopEvent();
31642                  this.selectNext();
31643              break;
31644              case e.UP:
31645                  e.stopEvent();
31646                  this.selectPrevious();
31647              break;
31648              case e.RIGHT:
31649                  e.preventDefault();
31650                  if(s.hasChildNodes()){
31651                      if(!s.isExpanded()){
31652                          s.expand();
31653                      }else if(s.firstChild){
31654                          this.select(s.firstChild, e);
31655                      }
31656                  }
31657              break;
31658              case e.LEFT:
31659                  e.preventDefault();
31660                  if(s.hasChildNodes() && s.isExpanded()){
31661                      s.collapse();
31662                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31663                      this.select(s.parentNode, e);
31664                  }
31665              break;
31666         };
31667     }
31668 });
31669
31670 /**
31671  * @class Roo.tree.MultiSelectionModel
31672  * @extends Roo.util.Observable
31673  * Multi selection for a TreePanel.
31674  * @param {Object} cfg Configuration
31675  */
31676 Roo.tree.MultiSelectionModel = function(){
31677    this.selNodes = [];
31678    this.selMap = {};
31679    this.addEvents({
31680        /**
31681         * @event selectionchange
31682         * Fires when the selected nodes change
31683         * @param {MultiSelectionModel} this
31684         * @param {Array} nodes Array of the selected nodes
31685         */
31686        "selectionchange" : true
31687    });
31688    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31689    
31690 };
31691
31692 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31693     init : function(tree){
31694         this.tree = tree;
31695         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31696         tree.on("click", this.onNodeClick, this);
31697     },
31698     
31699     onNodeClick : function(node, e){
31700         this.select(node, e, e.ctrlKey);
31701     },
31702     
31703     /**
31704      * Select a node.
31705      * @param {TreeNode} node The node to select
31706      * @param {EventObject} e (optional) An event associated with the selection
31707      * @param {Boolean} keepExisting True to retain existing selections
31708      * @return {TreeNode} The selected node
31709      */
31710     select : function(node, e, keepExisting){
31711         if(keepExisting !== true){
31712             this.clearSelections(true);
31713         }
31714         if(this.isSelected(node)){
31715             this.lastSelNode = node;
31716             return node;
31717         }
31718         this.selNodes.push(node);
31719         this.selMap[node.id] = node;
31720         this.lastSelNode = node;
31721         node.ui.onSelectedChange(true);
31722         this.fireEvent("selectionchange", this, this.selNodes);
31723         return node;
31724     },
31725     
31726     /**
31727      * Deselect a node.
31728      * @param {TreeNode} node The node to unselect
31729      */
31730     unselect : function(node){
31731         if(this.selMap[node.id]){
31732             node.ui.onSelectedChange(false);
31733             var sn = this.selNodes;
31734             var index = -1;
31735             if(sn.indexOf){
31736                 index = sn.indexOf(node);
31737             }else{
31738                 for(var i = 0, len = sn.length; i < len; i++){
31739                     if(sn[i] == node){
31740                         index = i;
31741                         break;
31742                     }
31743                 }
31744             }
31745             if(index != -1){
31746                 this.selNodes.splice(index, 1);
31747             }
31748             delete this.selMap[node.id];
31749             this.fireEvent("selectionchange", this, this.selNodes);
31750         }
31751     },
31752     
31753     /**
31754      * Clear all selections
31755      */
31756     clearSelections : function(suppressEvent){
31757         var sn = this.selNodes;
31758         if(sn.length > 0){
31759             for(var i = 0, len = sn.length; i < len; i++){
31760                 sn[i].ui.onSelectedChange(false);
31761             }
31762             this.selNodes = [];
31763             this.selMap = {};
31764             if(suppressEvent !== true){
31765                 this.fireEvent("selectionchange", this, this.selNodes);
31766             }
31767         }
31768     },
31769     
31770     /**
31771      * Returns true if the node is selected
31772      * @param {TreeNode} node The node to check
31773      * @return {Boolean}
31774      */
31775     isSelected : function(node){
31776         return this.selMap[node.id] ? true : false;  
31777     },
31778     
31779     /**
31780      * Returns an array of the selected nodes
31781      * @return {Array}
31782      */
31783     getSelectedNodes : function(){
31784         return this.selNodes;    
31785     },
31786
31787     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31788
31789     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31790
31791     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31792 });/*
31793  * Based on:
31794  * Ext JS Library 1.1.1
31795  * Copyright(c) 2006-2007, Ext JS, LLC.
31796  *
31797  * Originally Released Under LGPL - original licence link has changed is not relivant.
31798  *
31799  * Fork - LGPL
31800  * <script type="text/javascript">
31801  */
31802  
31803 /**
31804  * @class Roo.tree.TreeNode
31805  * @extends Roo.data.Node
31806  * @cfg {String} text The text for this node
31807  * @cfg {Boolean} expanded true to start the node expanded
31808  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31809  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31810  * @cfg {Boolean} disabled true to start the node disabled
31811  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31812  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31813  * @cfg {String} cls A css class to be added to the node
31814  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31815  * @cfg {String} href URL of the link used for the node (defaults to #)
31816  * @cfg {String} hrefTarget target frame for the link
31817  * @cfg {String} qtip An Ext QuickTip for the node
31818  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31819  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31820  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31821  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31822  * (defaults to undefined with no checkbox rendered)
31823  * @constructor
31824  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31825  */
31826 Roo.tree.TreeNode = function(attributes){
31827     attributes = attributes || {};
31828     if(typeof attributes == "string"){
31829         attributes = {text: attributes};
31830     }
31831     this.childrenRendered = false;
31832     this.rendered = false;
31833     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31834     this.expanded = attributes.expanded === true;
31835     this.isTarget = attributes.isTarget !== false;
31836     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31837     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31838
31839     /**
31840      * Read-only. The text for this node. To change it use setText().
31841      * @type String
31842      */
31843     this.text = attributes.text;
31844     /**
31845      * True if this node is disabled.
31846      * @type Boolean
31847      */
31848     this.disabled = attributes.disabled === true;
31849
31850     this.addEvents({
31851         /**
31852         * @event textchange
31853         * Fires when the text for this node is changed
31854         * @param {Node} this This node
31855         * @param {String} text The new text
31856         * @param {String} oldText The old text
31857         */
31858         "textchange" : true,
31859         /**
31860         * @event beforeexpand
31861         * Fires before this node is expanded, return false to cancel.
31862         * @param {Node} this This node
31863         * @param {Boolean} deep
31864         * @param {Boolean} anim
31865         */
31866         "beforeexpand" : true,
31867         /**
31868         * @event beforecollapse
31869         * Fires before this node is collapsed, return false to cancel.
31870         * @param {Node} this This node
31871         * @param {Boolean} deep
31872         * @param {Boolean} anim
31873         */
31874         "beforecollapse" : true,
31875         /**
31876         * @event expand
31877         * Fires when this node is expanded
31878         * @param {Node} this This node
31879         */
31880         "expand" : true,
31881         /**
31882         * @event disabledchange
31883         * Fires when the disabled status of this node changes
31884         * @param {Node} this This node
31885         * @param {Boolean} disabled
31886         */
31887         "disabledchange" : true,
31888         /**
31889         * @event collapse
31890         * Fires when this node is collapsed
31891         * @param {Node} this This node
31892         */
31893         "collapse" : true,
31894         /**
31895         * @event beforeclick
31896         * Fires before click processing. Return false to cancel the default action.
31897         * @param {Node} this This node
31898         * @param {Roo.EventObject} e The event object
31899         */
31900         "beforeclick":true,
31901         /**
31902         * @event checkchange
31903         * Fires when a node with a checkbox's checked property changes
31904         * @param {Node} this This node
31905         * @param {Boolean} checked
31906         */
31907         "checkchange":true,
31908         /**
31909         * @event click
31910         * Fires when this node is clicked
31911         * @param {Node} this This node
31912         * @param {Roo.EventObject} e The event object
31913         */
31914         "click":true,
31915         /**
31916         * @event dblclick
31917         * Fires when this node is double clicked
31918         * @param {Node} this This node
31919         * @param {Roo.EventObject} e The event object
31920         */
31921         "dblclick":true,
31922         /**
31923         * @event contextmenu
31924         * Fires when this node is right clicked
31925         * @param {Node} this This node
31926         * @param {Roo.EventObject} e The event object
31927         */
31928         "contextmenu":true,
31929         /**
31930         * @event beforechildrenrendered
31931         * Fires right before the child nodes for this node are rendered
31932         * @param {Node} this This node
31933         */
31934         "beforechildrenrendered":true
31935     });
31936
31937     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31938
31939     /**
31940      * Read-only. The UI for this node
31941      * @type TreeNodeUI
31942      */
31943     this.ui = new uiClass(this);
31944     
31945     // finally support items[]
31946     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31947         return;
31948     }
31949     
31950     
31951     Roo.each(this.attributes.items, function(c) {
31952         this.appendChild(Roo.factory(c,Roo.Tree));
31953     }, this);
31954     delete this.attributes.items;
31955     
31956     
31957     
31958 };
31959 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31960     preventHScroll: true,
31961     /**
31962      * Returns true if this node is expanded
31963      * @return {Boolean}
31964      */
31965     isExpanded : function(){
31966         return this.expanded;
31967     },
31968
31969     /**
31970      * Returns the UI object for this node
31971      * @return {TreeNodeUI}
31972      */
31973     getUI : function(){
31974         return this.ui;
31975     },
31976
31977     // private override
31978     setFirstChild : function(node){
31979         var of = this.firstChild;
31980         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31981         if(this.childrenRendered && of && node != of){
31982             of.renderIndent(true, true);
31983         }
31984         if(this.rendered){
31985             this.renderIndent(true, true);
31986         }
31987     },
31988
31989     // private override
31990     setLastChild : function(node){
31991         var ol = this.lastChild;
31992         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31993         if(this.childrenRendered && ol && node != ol){
31994             ol.renderIndent(true, true);
31995         }
31996         if(this.rendered){
31997             this.renderIndent(true, true);
31998         }
31999     },
32000
32001     // these methods are overridden to provide lazy rendering support
32002     // private override
32003     appendChild : function()
32004     {
32005         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32006         if(node && this.childrenRendered){
32007             node.render();
32008         }
32009         this.ui.updateExpandIcon();
32010         return node;
32011     },
32012
32013     // private override
32014     removeChild : function(node){
32015         this.ownerTree.getSelectionModel().unselect(node);
32016         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32017         // if it's been rendered remove dom node
32018         if(this.childrenRendered){
32019             node.ui.remove();
32020         }
32021         if(this.childNodes.length < 1){
32022             this.collapse(false, false);
32023         }else{
32024             this.ui.updateExpandIcon();
32025         }
32026         if(!this.firstChild) {
32027             this.childrenRendered = false;
32028         }
32029         return node;
32030     },
32031
32032     // private override
32033     insertBefore : function(node, refNode){
32034         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32035         if(newNode && refNode && this.childrenRendered){
32036             node.render();
32037         }
32038         this.ui.updateExpandIcon();
32039         return newNode;
32040     },
32041
32042     /**
32043      * Sets the text for this node
32044      * @param {String} text
32045      */
32046     setText : function(text){
32047         var oldText = this.text;
32048         this.text = text;
32049         this.attributes.text = text;
32050         if(this.rendered){ // event without subscribing
32051             this.ui.onTextChange(this, text, oldText);
32052         }
32053         this.fireEvent("textchange", this, text, oldText);
32054     },
32055
32056     /**
32057      * Triggers selection of this node
32058      */
32059     select : function(){
32060         this.getOwnerTree().getSelectionModel().select(this);
32061     },
32062
32063     /**
32064      * Triggers deselection of this node
32065      */
32066     unselect : function(){
32067         this.getOwnerTree().getSelectionModel().unselect(this);
32068     },
32069
32070     /**
32071      * Returns true if this node is selected
32072      * @return {Boolean}
32073      */
32074     isSelected : function(){
32075         return this.getOwnerTree().getSelectionModel().isSelected(this);
32076     },
32077
32078     /**
32079      * Expand this node.
32080      * @param {Boolean} deep (optional) True to expand all children as well
32081      * @param {Boolean} anim (optional) false to cancel the default animation
32082      * @param {Function} callback (optional) A callback to be called when
32083      * expanding this node completes (does not wait for deep expand to complete).
32084      * Called with 1 parameter, this node.
32085      */
32086     expand : function(deep, anim, callback){
32087         if(!this.expanded){
32088             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32089                 return;
32090             }
32091             if(!this.childrenRendered){
32092                 this.renderChildren();
32093             }
32094             this.expanded = true;
32095             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32096                 this.ui.animExpand(function(){
32097                     this.fireEvent("expand", this);
32098                     if(typeof callback == "function"){
32099                         callback(this);
32100                     }
32101                     if(deep === true){
32102                         this.expandChildNodes(true);
32103                     }
32104                 }.createDelegate(this));
32105                 return;
32106             }else{
32107                 this.ui.expand();
32108                 this.fireEvent("expand", this);
32109                 if(typeof callback == "function"){
32110                     callback(this);
32111                 }
32112             }
32113         }else{
32114            if(typeof callback == "function"){
32115                callback(this);
32116            }
32117         }
32118         if(deep === true){
32119             this.expandChildNodes(true);
32120         }
32121     },
32122
32123     isHiddenRoot : function(){
32124         return this.isRoot && !this.getOwnerTree().rootVisible;
32125     },
32126
32127     /**
32128      * Collapse this node.
32129      * @param {Boolean} deep (optional) True to collapse all children as well
32130      * @param {Boolean} anim (optional) false to cancel the default animation
32131      */
32132     collapse : function(deep, anim){
32133         if(this.expanded && !this.isHiddenRoot()){
32134             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32135                 return;
32136             }
32137             this.expanded = false;
32138             if((this.getOwnerTree().animate && anim !== false) || anim){
32139                 this.ui.animCollapse(function(){
32140                     this.fireEvent("collapse", this);
32141                     if(deep === true){
32142                         this.collapseChildNodes(true);
32143                     }
32144                 }.createDelegate(this));
32145                 return;
32146             }else{
32147                 this.ui.collapse();
32148                 this.fireEvent("collapse", this);
32149             }
32150         }
32151         if(deep === true){
32152             var cs = this.childNodes;
32153             for(var i = 0, len = cs.length; i < len; i++) {
32154                 cs[i].collapse(true, false);
32155             }
32156         }
32157     },
32158
32159     // private
32160     delayedExpand : function(delay){
32161         if(!this.expandProcId){
32162             this.expandProcId = this.expand.defer(delay, this);
32163         }
32164     },
32165
32166     // private
32167     cancelExpand : function(){
32168         if(this.expandProcId){
32169             clearTimeout(this.expandProcId);
32170         }
32171         this.expandProcId = false;
32172     },
32173
32174     /**
32175      * Toggles expanded/collapsed state of the node
32176      */
32177     toggle : function(){
32178         if(this.expanded){
32179             this.collapse();
32180         }else{
32181             this.expand();
32182         }
32183     },
32184
32185     /**
32186      * Ensures all parent nodes are expanded
32187      */
32188     ensureVisible : function(callback){
32189         var tree = this.getOwnerTree();
32190         tree.expandPath(this.parentNode.getPath(), false, function(){
32191             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32192             Roo.callback(callback);
32193         }.createDelegate(this));
32194     },
32195
32196     /**
32197      * Expand all child nodes
32198      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32199      */
32200     expandChildNodes : function(deep){
32201         var cs = this.childNodes;
32202         for(var i = 0, len = cs.length; i < len; i++) {
32203                 cs[i].expand(deep);
32204         }
32205     },
32206
32207     /**
32208      * Collapse all child nodes
32209      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32210      */
32211     collapseChildNodes : function(deep){
32212         var cs = this.childNodes;
32213         for(var i = 0, len = cs.length; i < len; i++) {
32214                 cs[i].collapse(deep);
32215         }
32216     },
32217
32218     /**
32219      * Disables this node
32220      */
32221     disable : function(){
32222         this.disabled = true;
32223         this.unselect();
32224         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32225             this.ui.onDisableChange(this, true);
32226         }
32227         this.fireEvent("disabledchange", this, true);
32228     },
32229
32230     /**
32231      * Enables this node
32232      */
32233     enable : function(){
32234         this.disabled = false;
32235         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32236             this.ui.onDisableChange(this, false);
32237         }
32238         this.fireEvent("disabledchange", this, false);
32239     },
32240
32241     // private
32242     renderChildren : function(suppressEvent){
32243         if(suppressEvent !== false){
32244             this.fireEvent("beforechildrenrendered", this);
32245         }
32246         var cs = this.childNodes;
32247         for(var i = 0, len = cs.length; i < len; i++){
32248             cs[i].render(true);
32249         }
32250         this.childrenRendered = true;
32251     },
32252
32253     // private
32254     sort : function(fn, scope){
32255         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32256         if(this.childrenRendered){
32257             var cs = this.childNodes;
32258             for(var i = 0, len = cs.length; i < len; i++){
32259                 cs[i].render(true);
32260             }
32261         }
32262     },
32263
32264     // private
32265     render : function(bulkRender){
32266         this.ui.render(bulkRender);
32267         if(!this.rendered){
32268             this.rendered = true;
32269             if(this.expanded){
32270                 this.expanded = false;
32271                 this.expand(false, false);
32272             }
32273         }
32274     },
32275
32276     // private
32277     renderIndent : function(deep, refresh){
32278         if(refresh){
32279             this.ui.childIndent = null;
32280         }
32281         this.ui.renderIndent();
32282         if(deep === true && this.childrenRendered){
32283             var cs = this.childNodes;
32284             for(var i = 0, len = cs.length; i < len; i++){
32285                 cs[i].renderIndent(true, refresh);
32286             }
32287         }
32288     }
32289 });/*
32290  * Based on:
32291  * Ext JS Library 1.1.1
32292  * Copyright(c) 2006-2007, Ext JS, LLC.
32293  *
32294  * Originally Released Under LGPL - original licence link has changed is not relivant.
32295  *
32296  * Fork - LGPL
32297  * <script type="text/javascript">
32298  */
32299  
32300 /**
32301  * @class Roo.tree.AsyncTreeNode
32302  * @extends Roo.tree.TreeNode
32303  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32304  * @constructor
32305  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32306  */
32307  Roo.tree.AsyncTreeNode = function(config){
32308     this.loaded = false;
32309     this.loading = false;
32310     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32311     /**
32312     * @event beforeload
32313     * Fires before this node is loaded, return false to cancel
32314     * @param {Node} this This node
32315     */
32316     this.addEvents({'beforeload':true, 'load': true});
32317     /**
32318     * @event load
32319     * Fires when this node is loaded
32320     * @param {Node} this This node
32321     */
32322     /**
32323      * The loader used by this node (defaults to using the tree's defined loader)
32324      * @type TreeLoader
32325      * @property loader
32326      */
32327 };
32328 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32329     expand : function(deep, anim, callback){
32330         if(this.loading){ // if an async load is already running, waiting til it's done
32331             var timer;
32332             var f = function(){
32333                 if(!this.loading){ // done loading
32334                     clearInterval(timer);
32335                     this.expand(deep, anim, callback);
32336                 }
32337             }.createDelegate(this);
32338             timer = setInterval(f, 200);
32339             return;
32340         }
32341         if(!this.loaded){
32342             if(this.fireEvent("beforeload", this) === false){
32343                 return;
32344             }
32345             this.loading = true;
32346             this.ui.beforeLoad(this);
32347             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32348             if(loader){
32349                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32350                 return;
32351             }
32352         }
32353         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32354     },
32355     
32356     /**
32357      * Returns true if this node is currently loading
32358      * @return {Boolean}
32359      */
32360     isLoading : function(){
32361         return this.loading;  
32362     },
32363     
32364     loadComplete : function(deep, anim, callback){
32365         this.loading = false;
32366         this.loaded = true;
32367         this.ui.afterLoad(this);
32368         this.fireEvent("load", this);
32369         this.expand(deep, anim, callback);
32370     },
32371     
32372     /**
32373      * Returns true if this node has been loaded
32374      * @return {Boolean}
32375      */
32376     isLoaded : function(){
32377         return this.loaded;
32378     },
32379     
32380     hasChildNodes : function(){
32381         if(!this.isLeaf() && !this.loaded){
32382             return true;
32383         }else{
32384             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32385         }
32386     },
32387
32388     /**
32389      * Trigger a reload for this node
32390      * @param {Function} callback
32391      */
32392     reload : function(callback){
32393         this.collapse(false, false);
32394         while(this.firstChild){
32395             this.removeChild(this.firstChild);
32396         }
32397         this.childrenRendered = false;
32398         this.loaded = false;
32399         if(this.isHiddenRoot()){
32400             this.expanded = false;
32401         }
32402         this.expand(false, false, callback);
32403     }
32404 });/*
32405  * Based on:
32406  * Ext JS Library 1.1.1
32407  * Copyright(c) 2006-2007, Ext JS, LLC.
32408  *
32409  * Originally Released Under LGPL - original licence link has changed is not relivant.
32410  *
32411  * Fork - LGPL
32412  * <script type="text/javascript">
32413  */
32414  
32415 /**
32416  * @class Roo.tree.TreeNodeUI
32417  * @constructor
32418  * @param {Object} node The node to render
32419  * The TreeNode UI implementation is separate from the
32420  * tree implementation. Unless you are customizing the tree UI,
32421  * you should never have to use this directly.
32422  */
32423 Roo.tree.TreeNodeUI = function(node){
32424     this.node = node;
32425     this.rendered = false;
32426     this.animating = false;
32427     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32428 };
32429
32430 Roo.tree.TreeNodeUI.prototype = {
32431     removeChild : function(node){
32432         if(this.rendered){
32433             this.ctNode.removeChild(node.ui.getEl());
32434         }
32435     },
32436
32437     beforeLoad : function(){
32438          this.addClass("x-tree-node-loading");
32439     },
32440
32441     afterLoad : function(){
32442          this.removeClass("x-tree-node-loading");
32443     },
32444
32445     onTextChange : function(node, text, oldText){
32446         if(this.rendered){
32447             this.textNode.innerHTML = text;
32448         }
32449     },
32450
32451     onDisableChange : function(node, state){
32452         this.disabled = state;
32453         if(state){
32454             this.addClass("x-tree-node-disabled");
32455         }else{
32456             this.removeClass("x-tree-node-disabled");
32457         }
32458     },
32459
32460     onSelectedChange : function(state){
32461         if(state){
32462             this.focus();
32463             this.addClass("x-tree-selected");
32464         }else{
32465             //this.blur();
32466             this.removeClass("x-tree-selected");
32467         }
32468     },
32469
32470     onMove : function(tree, node, oldParent, newParent, index, refNode){
32471         this.childIndent = null;
32472         if(this.rendered){
32473             var targetNode = newParent.ui.getContainer();
32474             if(!targetNode){//target not rendered
32475                 this.holder = document.createElement("div");
32476                 this.holder.appendChild(this.wrap);
32477                 return;
32478             }
32479             var insertBefore = refNode ? refNode.ui.getEl() : null;
32480             if(insertBefore){
32481                 targetNode.insertBefore(this.wrap, insertBefore);
32482             }else{
32483                 targetNode.appendChild(this.wrap);
32484             }
32485             this.node.renderIndent(true);
32486         }
32487     },
32488
32489     addClass : function(cls){
32490         if(this.elNode){
32491             Roo.fly(this.elNode).addClass(cls);
32492         }
32493     },
32494
32495     removeClass : function(cls){
32496         if(this.elNode){
32497             Roo.fly(this.elNode).removeClass(cls);
32498         }
32499     },
32500
32501     remove : function(){
32502         if(this.rendered){
32503             this.holder = document.createElement("div");
32504             this.holder.appendChild(this.wrap);
32505         }
32506     },
32507
32508     fireEvent : function(){
32509         return this.node.fireEvent.apply(this.node, arguments);
32510     },
32511
32512     initEvents : function(){
32513         this.node.on("move", this.onMove, this);
32514         var E = Roo.EventManager;
32515         var a = this.anchor;
32516
32517         var el = Roo.fly(a, '_treeui');
32518
32519         if(Roo.isOpera){ // opera render bug ignores the CSS
32520             el.setStyle("text-decoration", "none");
32521         }
32522
32523         el.on("click", this.onClick, this);
32524         el.on("dblclick", this.onDblClick, this);
32525
32526         if(this.checkbox){
32527             Roo.EventManager.on(this.checkbox,
32528                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32529         }
32530
32531         el.on("contextmenu", this.onContextMenu, this);
32532
32533         var icon = Roo.fly(this.iconNode);
32534         icon.on("click", this.onClick, this);
32535         icon.on("dblclick", this.onDblClick, this);
32536         icon.on("contextmenu", this.onContextMenu, this);
32537         E.on(this.ecNode, "click", this.ecClick, this, true);
32538
32539         if(this.node.disabled){
32540             this.addClass("x-tree-node-disabled");
32541         }
32542         if(this.node.hidden){
32543             this.addClass("x-tree-node-disabled");
32544         }
32545         var ot = this.node.getOwnerTree();
32546         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32547         if(dd && (!this.node.isRoot || ot.rootVisible)){
32548             Roo.dd.Registry.register(this.elNode, {
32549                 node: this.node,
32550                 handles: this.getDDHandles(),
32551                 isHandle: false
32552             });
32553         }
32554     },
32555
32556     getDDHandles : function(){
32557         return [this.iconNode, this.textNode];
32558     },
32559
32560     hide : function(){
32561         if(this.rendered){
32562             this.wrap.style.display = "none";
32563         }
32564     },
32565
32566     show : function(){
32567         if(this.rendered){
32568             this.wrap.style.display = "";
32569         }
32570     },
32571
32572     onContextMenu : function(e){
32573         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32574             e.preventDefault();
32575             this.focus();
32576             this.fireEvent("contextmenu", this.node, e);
32577         }
32578     },
32579
32580     onClick : function(e){
32581         if(this.dropping){
32582             e.stopEvent();
32583             return;
32584         }
32585         if(this.fireEvent("beforeclick", this.node, e) !== false){
32586             if(!this.disabled && this.node.attributes.href){
32587                 this.fireEvent("click", this.node, e);
32588                 return;
32589             }
32590             e.preventDefault();
32591             if(this.disabled){
32592                 return;
32593             }
32594
32595             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32596                 this.node.toggle();
32597             }
32598
32599             this.fireEvent("click", this.node, e);
32600         }else{
32601             e.stopEvent();
32602         }
32603     },
32604
32605     onDblClick : function(e){
32606         e.preventDefault();
32607         if(this.disabled){
32608             return;
32609         }
32610         if(this.checkbox){
32611             this.toggleCheck();
32612         }
32613         if(!this.animating && this.node.hasChildNodes()){
32614             this.node.toggle();
32615         }
32616         this.fireEvent("dblclick", this.node, e);
32617     },
32618
32619     onCheckChange : function(){
32620         var checked = this.checkbox.checked;
32621         this.node.attributes.checked = checked;
32622         this.fireEvent('checkchange', this.node, checked);
32623     },
32624
32625     ecClick : function(e){
32626         if(!this.animating && this.node.hasChildNodes()){
32627             this.node.toggle();
32628         }
32629     },
32630
32631     startDrop : function(){
32632         this.dropping = true;
32633     },
32634
32635     // delayed drop so the click event doesn't get fired on a drop
32636     endDrop : function(){
32637        setTimeout(function(){
32638            this.dropping = false;
32639        }.createDelegate(this), 50);
32640     },
32641
32642     expand : function(){
32643         this.updateExpandIcon();
32644         this.ctNode.style.display = "";
32645     },
32646
32647     focus : function(){
32648         if(!this.node.preventHScroll){
32649             try{this.anchor.focus();
32650             }catch(e){}
32651         }else if(!Roo.isIE){
32652             try{
32653                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32654                 var l = noscroll.scrollLeft;
32655                 this.anchor.focus();
32656                 noscroll.scrollLeft = l;
32657             }catch(e){}
32658         }
32659     },
32660
32661     toggleCheck : function(value){
32662         var cb = this.checkbox;
32663         if(cb){
32664             cb.checked = (value === undefined ? !cb.checked : value);
32665         }
32666     },
32667
32668     blur : function(){
32669         try{
32670             this.anchor.blur();
32671         }catch(e){}
32672     },
32673
32674     animExpand : function(callback){
32675         var ct = Roo.get(this.ctNode);
32676         ct.stopFx();
32677         if(!this.node.hasChildNodes()){
32678             this.updateExpandIcon();
32679             this.ctNode.style.display = "";
32680             Roo.callback(callback);
32681             return;
32682         }
32683         this.animating = true;
32684         this.updateExpandIcon();
32685
32686         ct.slideIn('t', {
32687            callback : function(){
32688                this.animating = false;
32689                Roo.callback(callback);
32690             },
32691             scope: this,
32692             duration: this.node.ownerTree.duration || .25
32693         });
32694     },
32695
32696     highlight : function(){
32697         var tree = this.node.getOwnerTree();
32698         Roo.fly(this.wrap).highlight(
32699             tree.hlColor || "C3DAF9",
32700             {endColor: tree.hlBaseColor}
32701         );
32702     },
32703
32704     collapse : function(){
32705         this.updateExpandIcon();
32706         this.ctNode.style.display = "none";
32707     },
32708
32709     animCollapse : function(callback){
32710         var ct = Roo.get(this.ctNode);
32711         ct.enableDisplayMode('block');
32712         ct.stopFx();
32713
32714         this.animating = true;
32715         this.updateExpandIcon();
32716
32717         ct.slideOut('t', {
32718             callback : function(){
32719                this.animating = false;
32720                Roo.callback(callback);
32721             },
32722             scope: this,
32723             duration: this.node.ownerTree.duration || .25
32724         });
32725     },
32726
32727     getContainer : function(){
32728         return this.ctNode;
32729     },
32730
32731     getEl : function(){
32732         return this.wrap;
32733     },
32734
32735     appendDDGhost : function(ghostNode){
32736         ghostNode.appendChild(this.elNode.cloneNode(true));
32737     },
32738
32739     getDDRepairXY : function(){
32740         return Roo.lib.Dom.getXY(this.iconNode);
32741     },
32742
32743     onRender : function(){
32744         this.render();
32745     },
32746
32747     render : function(bulkRender){
32748         var n = this.node, a = n.attributes;
32749         var targetNode = n.parentNode ?
32750               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32751
32752         if(!this.rendered){
32753             this.rendered = true;
32754
32755             this.renderElements(n, a, targetNode, bulkRender);
32756
32757             if(a.qtip){
32758                if(this.textNode.setAttributeNS){
32759                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32760                    if(a.qtipTitle){
32761                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32762                    }
32763                }else{
32764                    this.textNode.setAttribute("ext:qtip", a.qtip);
32765                    if(a.qtipTitle){
32766                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32767                    }
32768                }
32769             }else if(a.qtipCfg){
32770                 a.qtipCfg.target = Roo.id(this.textNode);
32771                 Roo.QuickTips.register(a.qtipCfg);
32772             }
32773             this.initEvents();
32774             if(!this.node.expanded){
32775                 this.updateExpandIcon();
32776             }
32777         }else{
32778             if(bulkRender === true) {
32779                 targetNode.appendChild(this.wrap);
32780             }
32781         }
32782     },
32783
32784     renderElements : function(n, a, targetNode, bulkRender)
32785     {
32786         // add some indent caching, this helps performance when rendering a large tree
32787         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32788         var t = n.getOwnerTree();
32789         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32790         if (typeof(n.attributes.html) != 'undefined') {
32791             txt = n.attributes.html;
32792         }
32793         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32794         var cb = typeof a.checked == 'boolean';
32795         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32796         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32797             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32798             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32799             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32800             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32801             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32802              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32803                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32804             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32805             "</li>"];
32806
32807         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32808             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32809                                 n.nextSibling.ui.getEl(), buf.join(""));
32810         }else{
32811             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32812         }
32813
32814         this.elNode = this.wrap.childNodes[0];
32815         this.ctNode = this.wrap.childNodes[1];
32816         var cs = this.elNode.childNodes;
32817         this.indentNode = cs[0];
32818         this.ecNode = cs[1];
32819         this.iconNode = cs[2];
32820         var index = 3;
32821         if(cb){
32822             this.checkbox = cs[3];
32823             index++;
32824         }
32825         this.anchor = cs[index];
32826         this.textNode = cs[index].firstChild;
32827     },
32828
32829     getAnchor : function(){
32830         return this.anchor;
32831     },
32832
32833     getTextEl : function(){
32834         return this.textNode;
32835     },
32836
32837     getIconEl : function(){
32838         return this.iconNode;
32839     },
32840
32841     isChecked : function(){
32842         return this.checkbox ? this.checkbox.checked : false;
32843     },
32844
32845     updateExpandIcon : function(){
32846         if(this.rendered){
32847             var n = this.node, c1, c2;
32848             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32849             var hasChild = n.hasChildNodes();
32850             if(hasChild){
32851                 if(n.expanded){
32852                     cls += "-minus";
32853                     c1 = "x-tree-node-collapsed";
32854                     c2 = "x-tree-node-expanded";
32855                 }else{
32856                     cls += "-plus";
32857                     c1 = "x-tree-node-expanded";
32858                     c2 = "x-tree-node-collapsed";
32859                 }
32860                 if(this.wasLeaf){
32861                     this.removeClass("x-tree-node-leaf");
32862                     this.wasLeaf = false;
32863                 }
32864                 if(this.c1 != c1 || this.c2 != c2){
32865                     Roo.fly(this.elNode).replaceClass(c1, c2);
32866                     this.c1 = c1; this.c2 = c2;
32867                 }
32868             }else{
32869                 // this changes non-leafs into leafs if they have no children.
32870                 // it's not very rational behaviour..
32871                 
32872                 if(!this.wasLeaf && this.node.leaf){
32873                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32874                     delete this.c1;
32875                     delete this.c2;
32876                     this.wasLeaf = true;
32877                 }
32878             }
32879             var ecc = "x-tree-ec-icon "+cls;
32880             if(this.ecc != ecc){
32881                 this.ecNode.className = ecc;
32882                 this.ecc = ecc;
32883             }
32884         }
32885     },
32886
32887     getChildIndent : function(){
32888         if(!this.childIndent){
32889             var buf = [];
32890             var p = this.node;
32891             while(p){
32892                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32893                     if(!p.isLast()) {
32894                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32895                     } else {
32896                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32897                     }
32898                 }
32899                 p = p.parentNode;
32900             }
32901             this.childIndent = buf.join("");
32902         }
32903         return this.childIndent;
32904     },
32905
32906     renderIndent : function(){
32907         if(this.rendered){
32908             var indent = "";
32909             var p = this.node.parentNode;
32910             if(p){
32911                 indent = p.ui.getChildIndent();
32912             }
32913             if(this.indentMarkup != indent){ // don't rerender if not required
32914                 this.indentNode.innerHTML = indent;
32915                 this.indentMarkup = indent;
32916             }
32917             this.updateExpandIcon();
32918         }
32919     }
32920 };
32921
32922 Roo.tree.RootTreeNodeUI = function(){
32923     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32924 };
32925 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32926     render : function(){
32927         if(!this.rendered){
32928             var targetNode = this.node.ownerTree.innerCt.dom;
32929             this.node.expanded = true;
32930             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32931             this.wrap = this.ctNode = targetNode.firstChild;
32932         }
32933     },
32934     collapse : function(){
32935     },
32936     expand : function(){
32937     }
32938 });/*
32939  * Based on:
32940  * Ext JS Library 1.1.1
32941  * Copyright(c) 2006-2007, Ext JS, LLC.
32942  *
32943  * Originally Released Under LGPL - original licence link has changed is not relivant.
32944  *
32945  * Fork - LGPL
32946  * <script type="text/javascript">
32947  */
32948 /**
32949  * @class Roo.tree.TreeLoader
32950  * @extends Roo.util.Observable
32951  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32952  * nodes from a specified URL. The response must be a javascript Array definition
32953  * who's elements are node definition objects. eg:
32954  * <pre><code>
32955 {  success : true,
32956    data :      [
32957    
32958     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32959     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32960     ]
32961 }
32962
32963
32964 </code></pre>
32965  * <br><br>
32966  * The old style respose with just an array is still supported, but not recommended.
32967  * <br><br>
32968  *
32969  * A server request is sent, and child nodes are loaded only when a node is expanded.
32970  * The loading node's id is passed to the server under the parameter name "node" to
32971  * enable the server to produce the correct child nodes.
32972  * <br><br>
32973  * To pass extra parameters, an event handler may be attached to the "beforeload"
32974  * event, and the parameters specified in the TreeLoader's baseParams property:
32975  * <pre><code>
32976     myTreeLoader.on("beforeload", function(treeLoader, node) {
32977         this.baseParams.category = node.attributes.category;
32978     }, this);
32979 </code></pre><
32980  * This would pass an HTTP parameter called "category" to the server containing
32981  * the value of the Node's "category" attribute.
32982  * @constructor
32983  * Creates a new Treeloader.
32984  * @param {Object} config A config object containing config properties.
32985  */
32986 Roo.tree.TreeLoader = function(config){
32987     this.baseParams = {};
32988     this.requestMethod = "POST";
32989     Roo.apply(this, config);
32990
32991     this.addEvents({
32992     
32993         /**
32994          * @event beforeload
32995          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32996          * @param {Object} This TreeLoader object.
32997          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32998          * @param {Object} callback The callback function specified in the {@link #load} call.
32999          */
33000         beforeload : true,
33001         /**
33002          * @event load
33003          * Fires when the node has been successfuly loaded.
33004          * @param {Object} This TreeLoader object.
33005          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33006          * @param {Object} response The response object containing the data from the server.
33007          */
33008         load : true,
33009         /**
33010          * @event loadexception
33011          * Fires if the network request failed.
33012          * @param {Object} This TreeLoader object.
33013          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33014          * @param {Object} response The response object containing the data from the server.
33015          */
33016         loadexception : true,
33017         /**
33018          * @event create
33019          * Fires before a node is created, enabling you to return custom Node types 
33020          * @param {Object} This TreeLoader object.
33021          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33022          */
33023         create : true
33024     });
33025
33026     Roo.tree.TreeLoader.superclass.constructor.call(this);
33027 };
33028
33029 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33030     /**
33031     * @cfg {String} dataUrl The URL from which to request a Json string which
33032     * specifies an array of node definition object representing the child nodes
33033     * to be loaded.
33034     */
33035     /**
33036     * @cfg {String} requestMethod either GET or POST
33037     * defaults to POST (due to BC)
33038     * to be loaded.
33039     */
33040     /**
33041     * @cfg {Object} baseParams (optional) An object containing properties which
33042     * specify HTTP parameters to be passed to each request for child nodes.
33043     */
33044     /**
33045     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33046     * created by this loader. If the attributes sent by the server have an attribute in this object,
33047     * they take priority.
33048     */
33049     /**
33050     * @cfg {Object} uiProviders (optional) An object containing properties which
33051     * 
33052     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33053     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33054     * <i>uiProvider</i> attribute of a returned child node is a string rather
33055     * than a reference to a TreeNodeUI implementation, this that string value
33056     * is used as a property name in the uiProviders object. You can define the provider named
33057     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33058     */
33059     uiProviders : {},
33060
33061     /**
33062     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33063     * child nodes before loading.
33064     */
33065     clearOnLoad : true,
33066
33067     /**
33068     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33069     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33070     * Grid query { data : [ .....] }
33071     */
33072     
33073     root : false,
33074      /**
33075     * @cfg {String} queryParam (optional) 
33076     * Name of the query as it will be passed on the querystring (defaults to 'node')
33077     * eg. the request will be ?node=[id]
33078     */
33079     
33080     
33081     queryParam: false,
33082     
33083     /**
33084      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33085      * This is called automatically when a node is expanded, but may be used to reload
33086      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33087      * @param {Roo.tree.TreeNode} node
33088      * @param {Function} callback
33089      */
33090     load : function(node, callback){
33091         if(this.clearOnLoad){
33092             while(node.firstChild){
33093                 node.removeChild(node.firstChild);
33094             }
33095         }
33096         if(node.attributes.children){ // preloaded json children
33097             var cs = node.attributes.children;
33098             for(var i = 0, len = cs.length; i < len; i++){
33099                 node.appendChild(this.createNode(cs[i]));
33100             }
33101             if(typeof callback == "function"){
33102                 callback();
33103             }
33104         }else if(this.dataUrl){
33105             this.requestData(node, callback);
33106         }
33107     },
33108
33109     getParams: function(node){
33110         var buf = [], bp = this.baseParams;
33111         for(var key in bp){
33112             if(typeof bp[key] != "function"){
33113                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33114             }
33115         }
33116         var n = this.queryParam === false ? 'node' : this.queryParam;
33117         buf.push(n + "=", encodeURIComponent(node.id));
33118         return buf.join("");
33119     },
33120
33121     requestData : function(node, callback){
33122         if(this.fireEvent("beforeload", this, node, callback) !== false){
33123             this.transId = Roo.Ajax.request({
33124                 method:this.requestMethod,
33125                 url: this.dataUrl||this.url,
33126                 success: this.handleResponse,
33127                 failure: this.handleFailure,
33128                 scope: this,
33129                 argument: {callback: callback, node: node},
33130                 params: this.getParams(node)
33131             });
33132         }else{
33133             // if the load is cancelled, make sure we notify
33134             // the node that we are done
33135             if(typeof callback == "function"){
33136                 callback();
33137             }
33138         }
33139     },
33140
33141     isLoading : function(){
33142         return this.transId ? true : false;
33143     },
33144
33145     abort : function(){
33146         if(this.isLoading()){
33147             Roo.Ajax.abort(this.transId);
33148         }
33149     },
33150
33151     // private
33152     createNode : function(attr)
33153     {
33154         // apply baseAttrs, nice idea Corey!
33155         if(this.baseAttrs){
33156             Roo.applyIf(attr, this.baseAttrs);
33157         }
33158         if(this.applyLoader !== false){
33159             attr.loader = this;
33160         }
33161         // uiProvider = depreciated..
33162         
33163         if(typeof(attr.uiProvider) == 'string'){
33164            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33165                 /**  eval:var:attr */ eval(attr.uiProvider);
33166         }
33167         if(typeof(this.uiProviders['default']) != 'undefined') {
33168             attr.uiProvider = this.uiProviders['default'];
33169         }
33170         
33171         this.fireEvent('create', this, attr);
33172         
33173         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33174         return(attr.leaf ?
33175                         new Roo.tree.TreeNode(attr) :
33176                         new Roo.tree.AsyncTreeNode(attr));
33177     },
33178
33179     processResponse : function(response, node, callback)
33180     {
33181         var json = response.responseText;
33182         try {
33183             
33184             var o = Roo.decode(json);
33185             
33186             if (this.root === false && typeof(o.success) != undefined) {
33187                 this.root = 'data'; // the default behaviour for list like data..
33188                 }
33189                 
33190             if (this.root !== false &&  !o.success) {
33191                 // it's a failure condition.
33192                 var a = response.argument;
33193                 this.fireEvent("loadexception", this, a.node, response);
33194                 Roo.log("Load failed - should have a handler really");
33195                 return;
33196             }
33197             
33198             
33199             
33200             if (this.root !== false) {
33201                  o = o[this.root];
33202             }
33203             
33204             for(var i = 0, len = o.length; i < len; i++){
33205                 var n = this.createNode(o[i]);
33206                 if(n){
33207                     node.appendChild(n);
33208                 }
33209             }
33210             if(typeof callback == "function"){
33211                 callback(this, node);
33212             }
33213         }catch(e){
33214             this.handleFailure(response);
33215         }
33216     },
33217
33218     handleResponse : function(response){
33219         this.transId = false;
33220         var a = response.argument;
33221         this.processResponse(response, a.node, a.callback);
33222         this.fireEvent("load", this, a.node, response);
33223     },
33224
33225     handleFailure : function(response)
33226     {
33227         // should handle failure better..
33228         this.transId = false;
33229         var a = response.argument;
33230         this.fireEvent("loadexception", this, a.node, response);
33231         if(typeof a.callback == "function"){
33232             a.callback(this, a.node);
33233         }
33234     }
33235 });/*
33236  * Based on:
33237  * Ext JS Library 1.1.1
33238  * Copyright(c) 2006-2007, Ext JS, LLC.
33239  *
33240  * Originally Released Under LGPL - original licence link has changed is not relivant.
33241  *
33242  * Fork - LGPL
33243  * <script type="text/javascript">
33244  */
33245
33246 /**
33247 * @class Roo.tree.TreeFilter
33248 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33249 * @param {TreePanel} tree
33250 * @param {Object} config (optional)
33251  */
33252 Roo.tree.TreeFilter = function(tree, config){
33253     this.tree = tree;
33254     this.filtered = {};
33255     Roo.apply(this, config);
33256 };
33257
33258 Roo.tree.TreeFilter.prototype = {
33259     clearBlank:false,
33260     reverse:false,
33261     autoClear:false,
33262     remove:false,
33263
33264      /**
33265      * Filter the data by a specific attribute.
33266      * @param {String/RegExp} value Either string that the attribute value
33267      * should start with or a RegExp to test against the attribute
33268      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33269      * @param {TreeNode} startNode (optional) The node to start the filter at.
33270      */
33271     filter : function(value, attr, startNode){
33272         attr = attr || "text";
33273         var f;
33274         if(typeof value == "string"){
33275             var vlen = value.length;
33276             // auto clear empty filter
33277             if(vlen == 0 && this.clearBlank){
33278                 this.clear();
33279                 return;
33280             }
33281             value = value.toLowerCase();
33282             f = function(n){
33283                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33284             };
33285         }else if(value.exec){ // regex?
33286             f = function(n){
33287                 return value.test(n.attributes[attr]);
33288             };
33289         }else{
33290             throw 'Illegal filter type, must be string or regex';
33291         }
33292         this.filterBy(f, null, startNode);
33293         },
33294
33295     /**
33296      * Filter by a function. The passed function will be called with each
33297      * node in the tree (or from the startNode). If the function returns true, the node is kept
33298      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33299      * @param {Function} fn The filter function
33300      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33301      */
33302     filterBy : function(fn, scope, startNode){
33303         startNode = startNode || this.tree.root;
33304         if(this.autoClear){
33305             this.clear();
33306         }
33307         var af = this.filtered, rv = this.reverse;
33308         var f = function(n){
33309             if(n == startNode){
33310                 return true;
33311             }
33312             if(af[n.id]){
33313                 return false;
33314             }
33315             var m = fn.call(scope || n, n);
33316             if(!m || rv){
33317                 af[n.id] = n;
33318                 n.ui.hide();
33319                 return false;
33320             }
33321             return true;
33322         };
33323         startNode.cascade(f);
33324         if(this.remove){
33325            for(var id in af){
33326                if(typeof id != "function"){
33327                    var n = af[id];
33328                    if(n && n.parentNode){
33329                        n.parentNode.removeChild(n);
33330                    }
33331                }
33332            }
33333         }
33334     },
33335
33336     /**
33337      * Clears the current filter. Note: with the "remove" option
33338      * set a filter cannot be cleared.
33339      */
33340     clear : function(){
33341         var t = this.tree;
33342         var af = this.filtered;
33343         for(var id in af){
33344             if(typeof id != "function"){
33345                 var n = af[id];
33346                 if(n){
33347                     n.ui.show();
33348                 }
33349             }
33350         }
33351         this.filtered = {};
33352     }
33353 };
33354 /*
33355  * Based on:
33356  * Ext JS Library 1.1.1
33357  * Copyright(c) 2006-2007, Ext JS, LLC.
33358  *
33359  * Originally Released Under LGPL - original licence link has changed is not relivant.
33360  *
33361  * Fork - LGPL
33362  * <script type="text/javascript">
33363  */
33364  
33365
33366 /**
33367  * @class Roo.tree.TreeSorter
33368  * Provides sorting of nodes in a TreePanel
33369  * 
33370  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33371  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33372  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33373  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33374  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33375  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33376  * @constructor
33377  * @param {TreePanel} tree
33378  * @param {Object} config
33379  */
33380 Roo.tree.TreeSorter = function(tree, config){
33381     Roo.apply(this, config);
33382     tree.on("beforechildrenrendered", this.doSort, this);
33383     tree.on("append", this.updateSort, this);
33384     tree.on("insert", this.updateSort, this);
33385     
33386     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33387     var p = this.property || "text";
33388     var sortType = this.sortType;
33389     var fs = this.folderSort;
33390     var cs = this.caseSensitive === true;
33391     var leafAttr = this.leafAttr || 'leaf';
33392
33393     this.sortFn = function(n1, n2){
33394         if(fs){
33395             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33396                 return 1;
33397             }
33398             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33399                 return -1;
33400             }
33401         }
33402         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33403         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33404         if(v1 < v2){
33405                         return dsc ? +1 : -1;
33406                 }else if(v1 > v2){
33407                         return dsc ? -1 : +1;
33408         }else{
33409                 return 0;
33410         }
33411     };
33412 };
33413
33414 Roo.tree.TreeSorter.prototype = {
33415     doSort : function(node){
33416         node.sort(this.sortFn);
33417     },
33418     
33419     compareNodes : function(n1, n2){
33420         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33421     },
33422     
33423     updateSort : function(tree, node){
33424         if(node.childrenRendered){
33425             this.doSort.defer(1, this, [node]);
33426         }
33427     }
33428 };/*
33429  * Based on:
33430  * Ext JS Library 1.1.1
33431  * Copyright(c) 2006-2007, Ext JS, LLC.
33432  *
33433  * Originally Released Under LGPL - original licence link has changed is not relivant.
33434  *
33435  * Fork - LGPL
33436  * <script type="text/javascript">
33437  */
33438
33439 if(Roo.dd.DropZone){
33440     
33441 Roo.tree.TreeDropZone = function(tree, config){
33442     this.allowParentInsert = false;
33443     this.allowContainerDrop = false;
33444     this.appendOnly = false;
33445     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33446     this.tree = tree;
33447     this.lastInsertClass = "x-tree-no-status";
33448     this.dragOverData = {};
33449 };
33450
33451 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33452     ddGroup : "TreeDD",
33453     
33454     expandDelay : 1000,
33455     
33456     expandNode : function(node){
33457         if(node.hasChildNodes() && !node.isExpanded()){
33458             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33459         }
33460     },
33461     
33462     queueExpand : function(node){
33463         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33464     },
33465     
33466     cancelExpand : function(){
33467         if(this.expandProcId){
33468             clearTimeout(this.expandProcId);
33469             this.expandProcId = false;
33470         }
33471     },
33472     
33473     isValidDropPoint : function(n, pt, dd, e, data){
33474         if(!n || !data){ return false; }
33475         var targetNode = n.node;
33476         var dropNode = data.node;
33477         // default drop rules
33478         if(!(targetNode && targetNode.isTarget && pt)){
33479             return false;
33480         }
33481         if(pt == "append" && targetNode.allowChildren === false){
33482             return false;
33483         }
33484         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33485             return false;
33486         }
33487         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33488             return false;
33489         }
33490         // reuse the object
33491         var overEvent = this.dragOverData;
33492         overEvent.tree = this.tree;
33493         overEvent.target = targetNode;
33494         overEvent.data = data;
33495         overEvent.point = pt;
33496         overEvent.source = dd;
33497         overEvent.rawEvent = e;
33498         overEvent.dropNode = dropNode;
33499         overEvent.cancel = false;  
33500         var result = this.tree.fireEvent("nodedragover", overEvent);
33501         return overEvent.cancel === false && result !== false;
33502     },
33503     
33504     getDropPoint : function(e, n, dd){
33505         var tn = n.node;
33506         if(tn.isRoot){
33507             return tn.allowChildren !== false ? "append" : false; // always append for root
33508         }
33509         var dragEl = n.ddel;
33510         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33511         var y = Roo.lib.Event.getPageY(e);
33512         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33513         
33514         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33515         var noAppend = tn.allowChildren === false;
33516         if(this.appendOnly || tn.parentNode.allowChildren === false){
33517             return noAppend ? false : "append";
33518         }
33519         var noBelow = false;
33520         if(!this.allowParentInsert){
33521             noBelow = tn.hasChildNodes() && tn.isExpanded();
33522         }
33523         var q = (b - t) / (noAppend ? 2 : 3);
33524         if(y >= t && y < (t + q)){
33525             return "above";
33526         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33527             return "below";
33528         }else{
33529             return "append";
33530         }
33531     },
33532     
33533     onNodeEnter : function(n, dd, e, data){
33534         this.cancelExpand();
33535     },
33536     
33537     onNodeOver : function(n, dd, e, data){
33538         var pt = this.getDropPoint(e, n, dd);
33539         var node = n.node;
33540         
33541         // auto node expand check
33542         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33543             this.queueExpand(node);
33544         }else if(pt != "append"){
33545             this.cancelExpand();
33546         }
33547         
33548         // set the insert point style on the target node
33549         var returnCls = this.dropNotAllowed;
33550         if(this.isValidDropPoint(n, pt, dd, e, data)){
33551            if(pt){
33552                var el = n.ddel;
33553                var cls;
33554                if(pt == "above"){
33555                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33556                    cls = "x-tree-drag-insert-above";
33557                }else if(pt == "below"){
33558                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33559                    cls = "x-tree-drag-insert-below";
33560                }else{
33561                    returnCls = "x-tree-drop-ok-append";
33562                    cls = "x-tree-drag-append";
33563                }
33564                if(this.lastInsertClass != cls){
33565                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33566                    this.lastInsertClass = cls;
33567                }
33568            }
33569        }
33570        return returnCls;
33571     },
33572     
33573     onNodeOut : function(n, dd, e, data){
33574         this.cancelExpand();
33575         this.removeDropIndicators(n);
33576     },
33577     
33578     onNodeDrop : function(n, dd, e, data){
33579         var point = this.getDropPoint(e, n, dd);
33580         var targetNode = n.node;
33581         targetNode.ui.startDrop();
33582         if(!this.isValidDropPoint(n, point, dd, e, data)){
33583             targetNode.ui.endDrop();
33584             return false;
33585         }
33586         // first try to find the drop node
33587         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33588         var dropEvent = {
33589             tree : this.tree,
33590             target: targetNode,
33591             data: data,
33592             point: point,
33593             source: dd,
33594             rawEvent: e,
33595             dropNode: dropNode,
33596             cancel: !dropNode   
33597         };
33598         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33599         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33600             targetNode.ui.endDrop();
33601             return false;
33602         }
33603         // allow target changing
33604         targetNode = dropEvent.target;
33605         if(point == "append" && !targetNode.isExpanded()){
33606             targetNode.expand(false, null, function(){
33607                 this.completeDrop(dropEvent);
33608             }.createDelegate(this));
33609         }else{
33610             this.completeDrop(dropEvent);
33611         }
33612         return true;
33613     },
33614     
33615     completeDrop : function(de){
33616         var ns = de.dropNode, p = de.point, t = de.target;
33617         if(!(ns instanceof Array)){
33618             ns = [ns];
33619         }
33620         var n;
33621         for(var i = 0, len = ns.length; i < len; i++){
33622             n = ns[i];
33623             if(p == "above"){
33624                 t.parentNode.insertBefore(n, t);
33625             }else if(p == "below"){
33626                 t.parentNode.insertBefore(n, t.nextSibling);
33627             }else{
33628                 t.appendChild(n);
33629             }
33630         }
33631         n.ui.focus();
33632         if(this.tree.hlDrop){
33633             n.ui.highlight();
33634         }
33635         t.ui.endDrop();
33636         this.tree.fireEvent("nodedrop", de);
33637     },
33638     
33639     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33640         if(this.tree.hlDrop){
33641             dropNode.ui.focus();
33642             dropNode.ui.highlight();
33643         }
33644         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33645     },
33646     
33647     getTree : function(){
33648         return this.tree;
33649     },
33650     
33651     removeDropIndicators : function(n){
33652         if(n && n.ddel){
33653             var el = n.ddel;
33654             Roo.fly(el).removeClass([
33655                     "x-tree-drag-insert-above",
33656                     "x-tree-drag-insert-below",
33657                     "x-tree-drag-append"]);
33658             this.lastInsertClass = "_noclass";
33659         }
33660     },
33661     
33662     beforeDragDrop : function(target, e, id){
33663         this.cancelExpand();
33664         return true;
33665     },
33666     
33667     afterRepair : function(data){
33668         if(data && Roo.enableFx){
33669             data.node.ui.highlight();
33670         }
33671         this.hideProxy();
33672     }    
33673 });
33674
33675 }
33676 /*
33677  * Based on:
33678  * Ext JS Library 1.1.1
33679  * Copyright(c) 2006-2007, Ext JS, LLC.
33680  *
33681  * Originally Released Under LGPL - original licence link has changed is not relivant.
33682  *
33683  * Fork - LGPL
33684  * <script type="text/javascript">
33685  */
33686  
33687
33688 if(Roo.dd.DragZone){
33689 Roo.tree.TreeDragZone = function(tree, config){
33690     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33691     this.tree = tree;
33692 };
33693
33694 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33695     ddGroup : "TreeDD",
33696     
33697     onBeforeDrag : function(data, e){
33698         var n = data.node;
33699         return n && n.draggable && !n.disabled;
33700     },
33701     
33702     onInitDrag : function(e){
33703         var data = this.dragData;
33704         this.tree.getSelectionModel().select(data.node);
33705         this.proxy.update("");
33706         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33707         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33708     },
33709     
33710     getRepairXY : function(e, data){
33711         return data.node.ui.getDDRepairXY();
33712     },
33713     
33714     onEndDrag : function(data, e){
33715         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33716     },
33717     
33718     onValidDrop : function(dd, e, id){
33719         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33720         this.hideProxy();
33721     },
33722     
33723     beforeInvalidDrop : function(e, id){
33724         // this scrolls the original position back into view
33725         var sm = this.tree.getSelectionModel();
33726         sm.clearSelections();
33727         sm.select(this.dragData.node);
33728     }
33729 });
33730 }/*
33731  * Based on:
33732  * Ext JS Library 1.1.1
33733  * Copyright(c) 2006-2007, Ext JS, LLC.
33734  *
33735  * Originally Released Under LGPL - original licence link has changed is not relivant.
33736  *
33737  * Fork - LGPL
33738  * <script type="text/javascript">
33739  */
33740 /**
33741  * @class Roo.tree.TreeEditor
33742  * @extends Roo.Editor
33743  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33744  * as the editor field.
33745  * @constructor
33746  * @param {Object} config (used to be the tree panel.)
33747  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33748  * 
33749  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33750  * @cfg {Roo.form.TextField|Object} field The field configuration
33751  *
33752  * 
33753  */
33754 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33755     var tree = config;
33756     var field;
33757     if (oldconfig) { // old style..
33758         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33759     } else {
33760         // new style..
33761         tree = config.tree;
33762         config.field = config.field  || {};
33763         config.field.xtype = 'TextField';
33764         field = Roo.factory(config.field, Roo.form);
33765     }
33766     config = config || {};
33767     
33768     
33769     this.addEvents({
33770         /**
33771          * @event beforenodeedit
33772          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33773          * false from the handler of this event.
33774          * @param {Editor} this
33775          * @param {Roo.tree.Node} node 
33776          */
33777         "beforenodeedit" : true
33778     });
33779     
33780     //Roo.log(config);
33781     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33782
33783     this.tree = tree;
33784
33785     tree.on('beforeclick', this.beforeNodeClick, this);
33786     tree.getTreeEl().on('mousedown', this.hide, this);
33787     this.on('complete', this.updateNode, this);
33788     this.on('beforestartedit', this.fitToTree, this);
33789     this.on('startedit', this.bindScroll, this, {delay:10});
33790     this.on('specialkey', this.onSpecialKey, this);
33791 };
33792
33793 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33794     /**
33795      * @cfg {String} alignment
33796      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33797      */
33798     alignment: "l-l",
33799     // inherit
33800     autoSize: false,
33801     /**
33802      * @cfg {Boolean} hideEl
33803      * True to hide the bound element while the editor is displayed (defaults to false)
33804      */
33805     hideEl : false,
33806     /**
33807      * @cfg {String} cls
33808      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33809      */
33810     cls: "x-small-editor x-tree-editor",
33811     /**
33812      * @cfg {Boolean} shim
33813      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33814      */
33815     shim:false,
33816     // inherit
33817     shadow:"frame",
33818     /**
33819      * @cfg {Number} maxWidth
33820      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33821      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33822      * scroll and client offsets into account prior to each edit.
33823      */
33824     maxWidth: 250,
33825
33826     editDelay : 350,
33827
33828     // private
33829     fitToTree : function(ed, el){
33830         var td = this.tree.getTreeEl().dom, nd = el.dom;
33831         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33832             td.scrollLeft = nd.offsetLeft;
33833         }
33834         var w = Math.min(
33835                 this.maxWidth,
33836                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33837         this.setSize(w, '');
33838         
33839         return this.fireEvent('beforenodeedit', this, this.editNode);
33840         
33841     },
33842
33843     // private
33844     triggerEdit : function(node){
33845         this.completeEdit();
33846         this.editNode = node;
33847         this.startEdit(node.ui.textNode, node.text);
33848     },
33849
33850     // private
33851     bindScroll : function(){
33852         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33853     },
33854
33855     // private
33856     beforeNodeClick : function(node, e){
33857         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33858         this.lastClick = new Date();
33859         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33860             e.stopEvent();
33861             this.triggerEdit(node);
33862             return false;
33863         }
33864         return true;
33865     },
33866
33867     // private
33868     updateNode : function(ed, value){
33869         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33870         this.editNode.setText(value);
33871     },
33872
33873     // private
33874     onHide : function(){
33875         Roo.tree.TreeEditor.superclass.onHide.call(this);
33876         if(this.editNode){
33877             this.editNode.ui.focus();
33878         }
33879     },
33880
33881     // private
33882     onSpecialKey : function(field, e){
33883         var k = e.getKey();
33884         if(k == e.ESC){
33885             e.stopEvent();
33886             this.cancelEdit();
33887         }else if(k == e.ENTER && !e.hasModifier()){
33888             e.stopEvent();
33889             this.completeEdit();
33890         }
33891     }
33892 });//<Script type="text/javascript">
33893 /*
33894  * Based on:
33895  * Ext JS Library 1.1.1
33896  * Copyright(c) 2006-2007, Ext JS, LLC.
33897  *
33898  * Originally Released Under LGPL - original licence link has changed is not relivant.
33899  *
33900  * Fork - LGPL
33901  * <script type="text/javascript">
33902  */
33903  
33904 /**
33905  * Not documented??? - probably should be...
33906  */
33907
33908 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33909     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33910     
33911     renderElements : function(n, a, targetNode, bulkRender){
33912         //consel.log("renderElements?");
33913         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33914
33915         var t = n.getOwnerTree();
33916         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33917         
33918         var cols = t.columns;
33919         var bw = t.borderWidth;
33920         var c = cols[0];
33921         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33922          var cb = typeof a.checked == "boolean";
33923         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33924         var colcls = 'x-t-' + tid + '-c0';
33925         var buf = [
33926             '<li class="x-tree-node">',
33927             
33928                 
33929                 '<div class="x-tree-node-el ', a.cls,'">',
33930                     // extran...
33931                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33932                 
33933                 
33934                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33935                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33936                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33937                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33938                            (a.iconCls ? ' '+a.iconCls : ''),
33939                            '" unselectable="on" />',
33940                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33941                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33942                              
33943                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33944                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33945                             '<span unselectable="on" qtip="' + tx + '">',
33946                              tx,
33947                              '</span></a>' ,
33948                     '</div>',
33949                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33950                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33951                  ];
33952         for(var i = 1, len = cols.length; i < len; i++){
33953             c = cols[i];
33954             colcls = 'x-t-' + tid + '-c' +i;
33955             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33956             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33957                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33958                       "</div>");
33959          }
33960          
33961          buf.push(
33962             '</a>',
33963             '<div class="x-clear"></div></div>',
33964             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33965             "</li>");
33966         
33967         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33968             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33969                                 n.nextSibling.ui.getEl(), buf.join(""));
33970         }else{
33971             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33972         }
33973         var el = this.wrap.firstChild;
33974         this.elRow = el;
33975         this.elNode = el.firstChild;
33976         this.ranchor = el.childNodes[1];
33977         this.ctNode = this.wrap.childNodes[1];
33978         var cs = el.firstChild.childNodes;
33979         this.indentNode = cs[0];
33980         this.ecNode = cs[1];
33981         this.iconNode = cs[2];
33982         var index = 3;
33983         if(cb){
33984             this.checkbox = cs[3];
33985             index++;
33986         }
33987         this.anchor = cs[index];
33988         
33989         this.textNode = cs[index].firstChild;
33990         
33991         //el.on("click", this.onClick, this);
33992         //el.on("dblclick", this.onDblClick, this);
33993         
33994         
33995        // console.log(this);
33996     },
33997     initEvents : function(){
33998         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33999         
34000             
34001         var a = this.ranchor;
34002
34003         var el = Roo.get(a);
34004
34005         if(Roo.isOpera){ // opera render bug ignores the CSS
34006             el.setStyle("text-decoration", "none");
34007         }
34008
34009         el.on("click", this.onClick, this);
34010         el.on("dblclick", this.onDblClick, this);
34011         el.on("contextmenu", this.onContextMenu, this);
34012         
34013     },
34014     
34015     /*onSelectedChange : function(state){
34016         if(state){
34017             this.focus();
34018             this.addClass("x-tree-selected");
34019         }else{
34020             //this.blur();
34021             this.removeClass("x-tree-selected");
34022         }
34023     },*/
34024     addClass : function(cls){
34025         if(this.elRow){
34026             Roo.fly(this.elRow).addClass(cls);
34027         }
34028         
34029     },
34030     
34031     
34032     removeClass : function(cls){
34033         if(this.elRow){
34034             Roo.fly(this.elRow).removeClass(cls);
34035         }
34036     }
34037
34038     
34039     
34040 });//<Script type="text/javascript">
34041
34042 /*
34043  * Based on:
34044  * Ext JS Library 1.1.1
34045  * Copyright(c) 2006-2007, Ext JS, LLC.
34046  *
34047  * Originally Released Under LGPL - original licence link has changed is not relivant.
34048  *
34049  * Fork - LGPL
34050  * <script type="text/javascript">
34051  */
34052  
34053
34054 /**
34055  * @class Roo.tree.ColumnTree
34056  * @extends Roo.data.TreePanel
34057  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34058  * @cfg {int} borderWidth  compined right/left border allowance
34059  * @constructor
34060  * @param {String/HTMLElement/Element} el The container element
34061  * @param {Object} config
34062  */
34063 Roo.tree.ColumnTree =  function(el, config)
34064 {
34065    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34066    this.addEvents({
34067         /**
34068         * @event resize
34069         * Fire this event on a container when it resizes
34070         * @param {int} w Width
34071         * @param {int} h Height
34072         */
34073        "resize" : true
34074     });
34075     this.on('resize', this.onResize, this);
34076 };
34077
34078 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34079     //lines:false,
34080     
34081     
34082     borderWidth: Roo.isBorderBox ? 0 : 2, 
34083     headEls : false,
34084     
34085     render : function(){
34086         // add the header.....
34087        
34088         Roo.tree.ColumnTree.superclass.render.apply(this);
34089         
34090         this.el.addClass('x-column-tree');
34091         
34092         this.headers = this.el.createChild(
34093             {cls:'x-tree-headers'},this.innerCt.dom);
34094    
34095         var cols = this.columns, c;
34096         var totalWidth = 0;
34097         this.headEls = [];
34098         var  len = cols.length;
34099         for(var i = 0; i < len; i++){
34100              c = cols[i];
34101              totalWidth += c.width;
34102             this.headEls.push(this.headers.createChild({
34103                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34104                  cn: {
34105                      cls:'x-tree-hd-text',
34106                      html: c.header
34107                  },
34108                  style:'width:'+(c.width-this.borderWidth)+'px;'
34109              }));
34110         }
34111         this.headers.createChild({cls:'x-clear'});
34112         // prevent floats from wrapping when clipped
34113         this.headers.setWidth(totalWidth);
34114         //this.innerCt.setWidth(totalWidth);
34115         this.innerCt.setStyle({ overflow: 'auto' });
34116         this.onResize(this.width, this.height);
34117              
34118         
34119     },
34120     onResize : function(w,h)
34121     {
34122         this.height = h;
34123         this.width = w;
34124         // resize cols..
34125         this.innerCt.setWidth(this.width);
34126         this.innerCt.setHeight(this.height-20);
34127         
34128         // headers...
34129         var cols = this.columns, c;
34130         var totalWidth = 0;
34131         var expEl = false;
34132         var len = cols.length;
34133         for(var i = 0; i < len; i++){
34134             c = cols[i];
34135             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34136                 // it's the expander..
34137                 expEl  = this.headEls[i];
34138                 continue;
34139             }
34140             totalWidth += c.width;
34141             
34142         }
34143         if (expEl) {
34144             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34145         }
34146         this.headers.setWidth(w-20);
34147
34148         
34149         
34150         
34151     }
34152 });
34153 /*
34154  * Based on:
34155  * Ext JS Library 1.1.1
34156  * Copyright(c) 2006-2007, Ext JS, LLC.
34157  *
34158  * Originally Released Under LGPL - original licence link has changed is not relivant.
34159  *
34160  * Fork - LGPL
34161  * <script type="text/javascript">
34162  */
34163  
34164 /**
34165  * @class Roo.menu.Menu
34166  * @extends Roo.util.Observable
34167  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34168  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34169  * @constructor
34170  * Creates a new Menu
34171  * @param {Object} config Configuration options
34172  */
34173 Roo.menu.Menu = function(config){
34174     Roo.apply(this, config);
34175     this.id = this.id || Roo.id();
34176     this.addEvents({
34177         /**
34178          * @event beforeshow
34179          * Fires before this menu is displayed
34180          * @param {Roo.menu.Menu} this
34181          */
34182         beforeshow : true,
34183         /**
34184          * @event beforehide
34185          * Fires before this menu is hidden
34186          * @param {Roo.menu.Menu} this
34187          */
34188         beforehide : true,
34189         /**
34190          * @event show
34191          * Fires after this menu is displayed
34192          * @param {Roo.menu.Menu} this
34193          */
34194         show : true,
34195         /**
34196          * @event hide
34197          * Fires after this menu is hidden
34198          * @param {Roo.menu.Menu} this
34199          */
34200         hide : true,
34201         /**
34202          * @event click
34203          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34204          * @param {Roo.menu.Menu} this
34205          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34206          * @param {Roo.EventObject} e
34207          */
34208         click : true,
34209         /**
34210          * @event mouseover
34211          * Fires when the mouse is hovering over this menu
34212          * @param {Roo.menu.Menu} this
34213          * @param {Roo.EventObject} e
34214          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34215          */
34216         mouseover : true,
34217         /**
34218          * @event mouseout
34219          * Fires when the mouse exits this menu
34220          * @param {Roo.menu.Menu} this
34221          * @param {Roo.EventObject} e
34222          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34223          */
34224         mouseout : true,
34225         /**
34226          * @event itemclick
34227          * Fires when a menu item contained in this menu is clicked
34228          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34229          * @param {Roo.EventObject} e
34230          */
34231         itemclick: true
34232     });
34233     if (this.registerMenu) {
34234         Roo.menu.MenuMgr.register(this);
34235     }
34236     
34237     var mis = this.items;
34238     this.items = new Roo.util.MixedCollection();
34239     if(mis){
34240         this.add.apply(this, mis);
34241     }
34242 };
34243
34244 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34245     /**
34246      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34247      */
34248     minWidth : 120,
34249     /**
34250      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34251      * for bottom-right shadow (defaults to "sides")
34252      */
34253     shadow : "sides",
34254     /**
34255      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34256      * this menu (defaults to "tl-tr?")
34257      */
34258     subMenuAlign : "tl-tr?",
34259     /**
34260      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34261      * relative to its element of origin (defaults to "tl-bl?")
34262      */
34263     defaultAlign : "tl-bl?",
34264     /**
34265      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34266      */
34267     allowOtherMenus : false,
34268     /**
34269      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34270      */
34271     registerMenu : true,
34272
34273     hidden:true,
34274
34275     // private
34276     render : function(){
34277         if(this.el){
34278             return;
34279         }
34280         var el = this.el = new Roo.Layer({
34281             cls: "x-menu",
34282             shadow:this.shadow,
34283             constrain: false,
34284             parentEl: this.parentEl || document.body,
34285             zindex:15000
34286         });
34287
34288         this.keyNav = new Roo.menu.MenuNav(this);
34289
34290         if(this.plain){
34291             el.addClass("x-menu-plain");
34292         }
34293         if(this.cls){
34294             el.addClass(this.cls);
34295         }
34296         // generic focus element
34297         this.focusEl = el.createChild({
34298             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34299         });
34300         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34301         ul.on("click", this.onClick, this);
34302         ul.on("mouseover", this.onMouseOver, this);
34303         ul.on("mouseout", this.onMouseOut, this);
34304         this.items.each(function(item){
34305             var li = document.createElement("li");
34306             li.className = "x-menu-list-item";
34307             ul.dom.appendChild(li);
34308             item.render(li, this);
34309         }, this);
34310         this.ul = ul;
34311         this.autoWidth();
34312     },
34313
34314     // private
34315     autoWidth : function(){
34316         var el = this.el, ul = this.ul;
34317         if(!el){
34318             return;
34319         }
34320         var w = this.width;
34321         if(w){
34322             el.setWidth(w);
34323         }else if(Roo.isIE){
34324             el.setWidth(this.minWidth);
34325             var t = el.dom.offsetWidth; // force recalc
34326             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34327         }
34328     },
34329
34330     // private
34331     delayAutoWidth : function(){
34332         if(this.rendered){
34333             if(!this.awTask){
34334                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34335             }
34336             this.awTask.delay(20);
34337         }
34338     },
34339
34340     // private
34341     findTargetItem : function(e){
34342         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34343         if(t && t.menuItemId){
34344             return this.items.get(t.menuItemId);
34345         }
34346     },
34347
34348     // private
34349     onClick : function(e){
34350         var t;
34351         if(t = this.findTargetItem(e)){
34352             t.onClick(e);
34353             this.fireEvent("click", this, t, e);
34354         }
34355     },
34356
34357     // private
34358     setActiveItem : function(item, autoExpand){
34359         if(item != this.activeItem){
34360             if(this.activeItem){
34361                 this.activeItem.deactivate();
34362             }
34363             this.activeItem = item;
34364             item.activate(autoExpand);
34365         }else if(autoExpand){
34366             item.expandMenu();
34367         }
34368     },
34369
34370     // private
34371     tryActivate : function(start, step){
34372         var items = this.items;
34373         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34374             var item = items.get(i);
34375             if(!item.disabled && item.canActivate){
34376                 this.setActiveItem(item, false);
34377                 return item;
34378             }
34379         }
34380         return false;
34381     },
34382
34383     // private
34384     onMouseOver : function(e){
34385         var t;
34386         if(t = this.findTargetItem(e)){
34387             if(t.canActivate && !t.disabled){
34388                 this.setActiveItem(t, true);
34389             }
34390         }
34391         this.fireEvent("mouseover", this, e, t);
34392     },
34393
34394     // private
34395     onMouseOut : function(e){
34396         var t;
34397         if(t = this.findTargetItem(e)){
34398             if(t == this.activeItem && t.shouldDeactivate(e)){
34399                 this.activeItem.deactivate();
34400                 delete this.activeItem;
34401             }
34402         }
34403         this.fireEvent("mouseout", this, e, t);
34404     },
34405
34406     /**
34407      * Read-only.  Returns true if the menu is currently displayed, else false.
34408      * @type Boolean
34409      */
34410     isVisible : function(){
34411         return this.el && !this.hidden;
34412     },
34413
34414     /**
34415      * Displays this menu relative to another element
34416      * @param {String/HTMLElement/Roo.Element} element The element to align to
34417      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34418      * the element (defaults to this.defaultAlign)
34419      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34420      */
34421     show : function(el, pos, parentMenu){
34422         this.parentMenu = parentMenu;
34423         if(!this.el){
34424             this.render();
34425         }
34426         this.fireEvent("beforeshow", this);
34427         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34428     },
34429
34430     /**
34431      * Displays this menu at a specific xy position
34432      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34433      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34434      */
34435     showAt : function(xy, parentMenu, /* private: */_e){
34436         this.parentMenu = parentMenu;
34437         if(!this.el){
34438             this.render();
34439         }
34440         if(_e !== false){
34441             this.fireEvent("beforeshow", this);
34442             xy = this.el.adjustForConstraints(xy);
34443         }
34444         this.el.setXY(xy);
34445         this.el.show();
34446         this.hidden = false;
34447         this.focus();
34448         this.fireEvent("show", this);
34449     },
34450
34451     focus : function(){
34452         if(!this.hidden){
34453             this.doFocus.defer(50, this);
34454         }
34455     },
34456
34457     doFocus : function(){
34458         if(!this.hidden){
34459             this.focusEl.focus();
34460         }
34461     },
34462
34463     /**
34464      * Hides this menu and optionally all parent menus
34465      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34466      */
34467     hide : function(deep){
34468         if(this.el && this.isVisible()){
34469             this.fireEvent("beforehide", this);
34470             if(this.activeItem){
34471                 this.activeItem.deactivate();
34472                 this.activeItem = null;
34473             }
34474             this.el.hide();
34475             this.hidden = true;
34476             this.fireEvent("hide", this);
34477         }
34478         if(deep === true && this.parentMenu){
34479             this.parentMenu.hide(true);
34480         }
34481     },
34482
34483     /**
34484      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34485      * Any of the following are valid:
34486      * <ul>
34487      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34488      * <li>An HTMLElement object which will be converted to a menu item</li>
34489      * <li>A menu item config object that will be created as a new menu item</li>
34490      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34491      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34492      * </ul>
34493      * Usage:
34494      * <pre><code>
34495 // Create the menu
34496 var menu = new Roo.menu.Menu();
34497
34498 // Create a menu item to add by reference
34499 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34500
34501 // Add a bunch of items at once using different methods.
34502 // Only the last item added will be returned.
34503 var item = menu.add(
34504     menuItem,                // add existing item by ref
34505     'Dynamic Item',          // new TextItem
34506     '-',                     // new separator
34507     { text: 'Config Item' }  // new item by config
34508 );
34509 </code></pre>
34510      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34511      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34512      */
34513     add : function(){
34514         var a = arguments, l = a.length, item;
34515         for(var i = 0; i < l; i++){
34516             var el = a[i];
34517             if ((typeof(el) == "object") && el.xtype && el.xns) {
34518                 el = Roo.factory(el, Roo.menu);
34519             }
34520             
34521             if(el.render){ // some kind of Item
34522                 item = this.addItem(el);
34523             }else if(typeof el == "string"){ // string
34524                 if(el == "separator" || el == "-"){
34525                     item = this.addSeparator();
34526                 }else{
34527                     item = this.addText(el);
34528                 }
34529             }else if(el.tagName || el.el){ // element
34530                 item = this.addElement(el);
34531             }else if(typeof el == "object"){ // must be menu item config?
34532                 item = this.addMenuItem(el);
34533             }
34534         }
34535         return item;
34536     },
34537
34538     /**
34539      * Returns this menu's underlying {@link Roo.Element} object
34540      * @return {Roo.Element} The element
34541      */
34542     getEl : function(){
34543         if(!this.el){
34544             this.render();
34545         }
34546         return this.el;
34547     },
34548
34549     /**
34550      * Adds a separator bar to the menu
34551      * @return {Roo.menu.Item} The menu item that was added
34552      */
34553     addSeparator : function(){
34554         return this.addItem(new Roo.menu.Separator());
34555     },
34556
34557     /**
34558      * Adds an {@link Roo.Element} object to the menu
34559      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34560      * @return {Roo.menu.Item} The menu item that was added
34561      */
34562     addElement : function(el){
34563         return this.addItem(new Roo.menu.BaseItem(el));
34564     },
34565
34566     /**
34567      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34568      * @param {Roo.menu.Item} item The menu item to add
34569      * @return {Roo.menu.Item} The menu item that was added
34570      */
34571     addItem : function(item){
34572         this.items.add(item);
34573         if(this.ul){
34574             var li = document.createElement("li");
34575             li.className = "x-menu-list-item";
34576             this.ul.dom.appendChild(li);
34577             item.render(li, this);
34578             this.delayAutoWidth();
34579         }
34580         return item;
34581     },
34582
34583     /**
34584      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34585      * @param {Object} config A MenuItem config object
34586      * @return {Roo.menu.Item} The menu item that was added
34587      */
34588     addMenuItem : function(config){
34589         if(!(config instanceof Roo.menu.Item)){
34590             if(typeof config.checked == "boolean"){ // must be check menu item config?
34591                 config = new Roo.menu.CheckItem(config);
34592             }else{
34593                 config = new Roo.menu.Item(config);
34594             }
34595         }
34596         return this.addItem(config);
34597     },
34598
34599     /**
34600      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34601      * @param {String} text The text to display in the menu item
34602      * @return {Roo.menu.Item} The menu item that was added
34603      */
34604     addText : function(text){
34605         return this.addItem(new Roo.menu.TextItem({ text : text }));
34606     },
34607
34608     /**
34609      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34610      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34611      * @param {Roo.menu.Item} item The menu item to add
34612      * @return {Roo.menu.Item} The menu item that was added
34613      */
34614     insert : function(index, item){
34615         this.items.insert(index, item);
34616         if(this.ul){
34617             var li = document.createElement("li");
34618             li.className = "x-menu-list-item";
34619             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34620             item.render(li, this);
34621             this.delayAutoWidth();
34622         }
34623         return item;
34624     },
34625
34626     /**
34627      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34628      * @param {Roo.menu.Item} item The menu item to remove
34629      */
34630     remove : function(item){
34631         this.items.removeKey(item.id);
34632         item.destroy();
34633     },
34634
34635     /**
34636      * Removes and destroys all items in the menu
34637      */
34638     removeAll : function(){
34639         var f;
34640         while(f = this.items.first()){
34641             this.remove(f);
34642         }
34643     }
34644 });
34645
34646 // MenuNav is a private utility class used internally by the Menu
34647 Roo.menu.MenuNav = function(menu){
34648     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34649     this.scope = this.menu = menu;
34650 };
34651
34652 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34653     doRelay : function(e, h){
34654         var k = e.getKey();
34655         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34656             this.menu.tryActivate(0, 1);
34657             return false;
34658         }
34659         return h.call(this.scope || this, e, this.menu);
34660     },
34661
34662     up : function(e, m){
34663         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34664             m.tryActivate(m.items.length-1, -1);
34665         }
34666     },
34667
34668     down : function(e, m){
34669         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34670             m.tryActivate(0, 1);
34671         }
34672     },
34673
34674     right : function(e, m){
34675         if(m.activeItem){
34676             m.activeItem.expandMenu(true);
34677         }
34678     },
34679
34680     left : function(e, m){
34681         m.hide();
34682         if(m.parentMenu && m.parentMenu.activeItem){
34683             m.parentMenu.activeItem.activate();
34684         }
34685     },
34686
34687     enter : function(e, m){
34688         if(m.activeItem){
34689             e.stopPropagation();
34690             m.activeItem.onClick(e);
34691             m.fireEvent("click", this, m.activeItem);
34692             return true;
34693         }
34694     }
34695 });/*
34696  * Based on:
34697  * Ext JS Library 1.1.1
34698  * Copyright(c) 2006-2007, Ext JS, LLC.
34699  *
34700  * Originally Released Under LGPL - original licence link has changed is not relivant.
34701  *
34702  * Fork - LGPL
34703  * <script type="text/javascript">
34704  */
34705  
34706 /**
34707  * @class Roo.menu.MenuMgr
34708  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34709  * @singleton
34710  */
34711 Roo.menu.MenuMgr = function(){
34712    var menus, active, groups = {}, attached = false, lastShow = new Date();
34713
34714    // private - called when first menu is created
34715    function init(){
34716        menus = {};
34717        active = new Roo.util.MixedCollection();
34718        Roo.get(document).addKeyListener(27, function(){
34719            if(active.length > 0){
34720                hideAll();
34721            }
34722        });
34723    }
34724
34725    // private
34726    function hideAll(){
34727        if(active && active.length > 0){
34728            var c = active.clone();
34729            c.each(function(m){
34730                m.hide();
34731            });
34732        }
34733    }
34734
34735    // private
34736    function onHide(m){
34737        active.remove(m);
34738        if(active.length < 1){
34739            Roo.get(document).un("mousedown", onMouseDown);
34740            attached = false;
34741        }
34742    }
34743
34744    // private
34745    function onShow(m){
34746        var last = active.last();
34747        lastShow = new Date();
34748        active.add(m);
34749        if(!attached){
34750            Roo.get(document).on("mousedown", onMouseDown);
34751            attached = true;
34752        }
34753        if(m.parentMenu){
34754           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34755           m.parentMenu.activeChild = m;
34756        }else if(last && last.isVisible()){
34757           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34758        }
34759    }
34760
34761    // private
34762    function onBeforeHide(m){
34763        if(m.activeChild){
34764            m.activeChild.hide();
34765        }
34766        if(m.autoHideTimer){
34767            clearTimeout(m.autoHideTimer);
34768            delete m.autoHideTimer;
34769        }
34770    }
34771
34772    // private
34773    function onBeforeShow(m){
34774        var pm = m.parentMenu;
34775        if(!pm && !m.allowOtherMenus){
34776            hideAll();
34777        }else if(pm && pm.activeChild && active != m){
34778            pm.activeChild.hide();
34779        }
34780    }
34781
34782    // private
34783    function onMouseDown(e){
34784        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34785            hideAll();
34786        }
34787    }
34788
34789    // private
34790    function onBeforeCheck(mi, state){
34791        if(state){
34792            var g = groups[mi.group];
34793            for(var i = 0, l = g.length; i < l; i++){
34794                if(g[i] != mi){
34795                    g[i].setChecked(false);
34796                }
34797            }
34798        }
34799    }
34800
34801    return {
34802
34803        /**
34804         * Hides all menus that are currently visible
34805         */
34806        hideAll : function(){
34807             hideAll();  
34808        },
34809
34810        // private
34811        register : function(menu){
34812            if(!menus){
34813                init();
34814            }
34815            menus[menu.id] = menu;
34816            menu.on("beforehide", onBeforeHide);
34817            menu.on("hide", onHide);
34818            menu.on("beforeshow", onBeforeShow);
34819            menu.on("show", onShow);
34820            var g = menu.group;
34821            if(g && menu.events["checkchange"]){
34822                if(!groups[g]){
34823                    groups[g] = [];
34824                }
34825                groups[g].push(menu);
34826                menu.on("checkchange", onCheck);
34827            }
34828        },
34829
34830         /**
34831          * Returns a {@link Roo.menu.Menu} object
34832          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34833          * be used to generate and return a new Menu instance.
34834          */
34835        get : function(menu){
34836            if(typeof menu == "string"){ // menu id
34837                return menus[menu];
34838            }else if(menu.events){  // menu instance
34839                return menu;
34840            }else if(typeof menu.length == 'number'){ // array of menu items?
34841                return new Roo.menu.Menu({items:menu});
34842            }else{ // otherwise, must be a config
34843                return new Roo.menu.Menu(menu);
34844            }
34845        },
34846
34847        // private
34848        unregister : function(menu){
34849            delete menus[menu.id];
34850            menu.un("beforehide", onBeforeHide);
34851            menu.un("hide", onHide);
34852            menu.un("beforeshow", onBeforeShow);
34853            menu.un("show", onShow);
34854            var g = menu.group;
34855            if(g && menu.events["checkchange"]){
34856                groups[g].remove(menu);
34857                menu.un("checkchange", onCheck);
34858            }
34859        },
34860
34861        // private
34862        registerCheckable : function(menuItem){
34863            var g = menuItem.group;
34864            if(g){
34865                if(!groups[g]){
34866                    groups[g] = [];
34867                }
34868                groups[g].push(menuItem);
34869                menuItem.on("beforecheckchange", onBeforeCheck);
34870            }
34871        },
34872
34873        // private
34874        unregisterCheckable : function(menuItem){
34875            var g = menuItem.group;
34876            if(g){
34877                groups[g].remove(menuItem);
34878                menuItem.un("beforecheckchange", onBeforeCheck);
34879            }
34880        }
34881    };
34882 }();/*
34883  * Based on:
34884  * Ext JS Library 1.1.1
34885  * Copyright(c) 2006-2007, Ext JS, LLC.
34886  *
34887  * Originally Released Under LGPL - original licence link has changed is not relivant.
34888  *
34889  * Fork - LGPL
34890  * <script type="text/javascript">
34891  */
34892  
34893
34894 /**
34895  * @class Roo.menu.BaseItem
34896  * @extends Roo.Component
34897  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34898  * management and base configuration options shared by all menu components.
34899  * @constructor
34900  * Creates a new BaseItem
34901  * @param {Object} config Configuration options
34902  */
34903 Roo.menu.BaseItem = function(config){
34904     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34905
34906     this.addEvents({
34907         /**
34908          * @event click
34909          * Fires when this item is clicked
34910          * @param {Roo.menu.BaseItem} this
34911          * @param {Roo.EventObject} e
34912          */
34913         click: true,
34914         /**
34915          * @event activate
34916          * Fires when this item is activated
34917          * @param {Roo.menu.BaseItem} this
34918          */
34919         activate : true,
34920         /**
34921          * @event deactivate
34922          * Fires when this item is deactivated
34923          * @param {Roo.menu.BaseItem} this
34924          */
34925         deactivate : true
34926     });
34927
34928     if(this.handler){
34929         this.on("click", this.handler, this.scope, true);
34930     }
34931 };
34932
34933 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34934     /**
34935      * @cfg {Function} handler
34936      * A function that will handle the click event of this menu item (defaults to undefined)
34937      */
34938     /**
34939      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34940      */
34941     canActivate : false,
34942     /**
34943      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34944      */
34945     activeClass : "x-menu-item-active",
34946     /**
34947      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34948      */
34949     hideOnClick : true,
34950     /**
34951      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34952      */
34953     hideDelay : 100,
34954
34955     // private
34956     ctype: "Roo.menu.BaseItem",
34957
34958     // private
34959     actionMode : "container",
34960
34961     // private
34962     render : function(container, parentMenu){
34963         this.parentMenu = parentMenu;
34964         Roo.menu.BaseItem.superclass.render.call(this, container);
34965         this.container.menuItemId = this.id;
34966     },
34967
34968     // private
34969     onRender : function(container, position){
34970         this.el = Roo.get(this.el);
34971         container.dom.appendChild(this.el.dom);
34972     },
34973
34974     // private
34975     onClick : function(e){
34976         if(!this.disabled && this.fireEvent("click", this, e) !== false
34977                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34978             this.handleClick(e);
34979         }else{
34980             e.stopEvent();
34981         }
34982     },
34983
34984     // private
34985     activate : function(){
34986         if(this.disabled){
34987             return false;
34988         }
34989         var li = this.container;
34990         li.addClass(this.activeClass);
34991         this.region = li.getRegion().adjust(2, 2, -2, -2);
34992         this.fireEvent("activate", this);
34993         return true;
34994     },
34995
34996     // private
34997     deactivate : function(){
34998         this.container.removeClass(this.activeClass);
34999         this.fireEvent("deactivate", this);
35000     },
35001
35002     // private
35003     shouldDeactivate : function(e){
35004         return !this.region || !this.region.contains(e.getPoint());
35005     },
35006
35007     // private
35008     handleClick : function(e){
35009         if(this.hideOnClick){
35010             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35011         }
35012     },
35013
35014     // private
35015     expandMenu : function(autoActivate){
35016         // do nothing
35017     },
35018
35019     // private
35020     hideMenu : function(){
35021         // do nothing
35022     }
35023 });/*
35024  * Based on:
35025  * Ext JS Library 1.1.1
35026  * Copyright(c) 2006-2007, Ext JS, LLC.
35027  *
35028  * Originally Released Under LGPL - original licence link has changed is not relivant.
35029  *
35030  * Fork - LGPL
35031  * <script type="text/javascript">
35032  */
35033  
35034 /**
35035  * @class Roo.menu.Adapter
35036  * @extends Roo.menu.BaseItem
35037  * 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.
35038  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35039  * @constructor
35040  * Creates a new Adapter
35041  * @param {Object} config Configuration options
35042  */
35043 Roo.menu.Adapter = function(component, config){
35044     Roo.menu.Adapter.superclass.constructor.call(this, config);
35045     this.component = component;
35046 };
35047 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35048     // private
35049     canActivate : true,
35050
35051     // private
35052     onRender : function(container, position){
35053         this.component.render(container);
35054         this.el = this.component.getEl();
35055     },
35056
35057     // private
35058     activate : function(){
35059         if(this.disabled){
35060             return false;
35061         }
35062         this.component.focus();
35063         this.fireEvent("activate", this);
35064         return true;
35065     },
35066
35067     // private
35068     deactivate : function(){
35069         this.fireEvent("deactivate", this);
35070     },
35071
35072     // private
35073     disable : function(){
35074         this.component.disable();
35075         Roo.menu.Adapter.superclass.disable.call(this);
35076     },
35077
35078     // private
35079     enable : function(){
35080         this.component.enable();
35081         Roo.menu.Adapter.superclass.enable.call(this);
35082     }
35083 });/*
35084  * Based on:
35085  * Ext JS Library 1.1.1
35086  * Copyright(c) 2006-2007, Ext JS, LLC.
35087  *
35088  * Originally Released Under LGPL - original licence link has changed is not relivant.
35089  *
35090  * Fork - LGPL
35091  * <script type="text/javascript">
35092  */
35093
35094 /**
35095  * @class Roo.menu.TextItem
35096  * @extends Roo.menu.BaseItem
35097  * Adds a static text string to a menu, usually used as either a heading or group separator.
35098  * Note: old style constructor with text is still supported.
35099  * 
35100  * @constructor
35101  * Creates a new TextItem
35102  * @param {Object} cfg Configuration
35103  */
35104 Roo.menu.TextItem = function(cfg){
35105     if (typeof(cfg) == 'string') {
35106         this.text = cfg;
35107     } else {
35108         Roo.apply(this,cfg);
35109     }
35110     
35111     Roo.menu.TextItem.superclass.constructor.call(this);
35112 };
35113
35114 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35115     /**
35116      * @cfg {Boolean} text Text to show on item.
35117      */
35118     text : '',
35119     
35120     /**
35121      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35122      */
35123     hideOnClick : false,
35124     /**
35125      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35126      */
35127     itemCls : "x-menu-text",
35128
35129     // private
35130     onRender : function(){
35131         var s = document.createElement("span");
35132         s.className = this.itemCls;
35133         s.innerHTML = this.text;
35134         this.el = s;
35135         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35136     }
35137 });/*
35138  * Based on:
35139  * Ext JS Library 1.1.1
35140  * Copyright(c) 2006-2007, Ext JS, LLC.
35141  *
35142  * Originally Released Under LGPL - original licence link has changed is not relivant.
35143  *
35144  * Fork - LGPL
35145  * <script type="text/javascript">
35146  */
35147
35148 /**
35149  * @class Roo.menu.Separator
35150  * @extends Roo.menu.BaseItem
35151  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35152  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35153  * @constructor
35154  * @param {Object} config Configuration options
35155  */
35156 Roo.menu.Separator = function(config){
35157     Roo.menu.Separator.superclass.constructor.call(this, config);
35158 };
35159
35160 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35161     /**
35162      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35163      */
35164     itemCls : "x-menu-sep",
35165     /**
35166      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35167      */
35168     hideOnClick : false,
35169
35170     // private
35171     onRender : function(li){
35172         var s = document.createElement("span");
35173         s.className = this.itemCls;
35174         s.innerHTML = "&#160;";
35175         this.el = s;
35176         li.addClass("x-menu-sep-li");
35177         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35178     }
35179 });/*
35180  * Based on:
35181  * Ext JS Library 1.1.1
35182  * Copyright(c) 2006-2007, Ext JS, LLC.
35183  *
35184  * Originally Released Under LGPL - original licence link has changed is not relivant.
35185  *
35186  * Fork - LGPL
35187  * <script type="text/javascript">
35188  */
35189 /**
35190  * @class Roo.menu.Item
35191  * @extends Roo.menu.BaseItem
35192  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35193  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35194  * activation and click handling.
35195  * @constructor
35196  * Creates a new Item
35197  * @param {Object} config Configuration options
35198  */
35199 Roo.menu.Item = function(config){
35200     Roo.menu.Item.superclass.constructor.call(this, config);
35201     if(this.menu){
35202         this.menu = Roo.menu.MenuMgr.get(this.menu);
35203     }
35204 };
35205 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35206     
35207     /**
35208      * @cfg {String} text
35209      * The text to show on the menu item.
35210      */
35211     text: '',
35212      /**
35213      * @cfg {String} HTML to render in menu
35214      * The text to show on the menu item (HTML version).
35215      */
35216     html: '',
35217     /**
35218      * @cfg {String} icon
35219      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35220      */
35221     icon: undefined,
35222     /**
35223      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35224      */
35225     itemCls : "x-menu-item",
35226     /**
35227      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35228      */
35229     canActivate : true,
35230     /**
35231      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35232      */
35233     showDelay: 200,
35234     // doc'd in BaseItem
35235     hideDelay: 200,
35236
35237     // private
35238     ctype: "Roo.menu.Item",
35239     
35240     // private
35241     onRender : function(container, position){
35242         var el = document.createElement("a");
35243         el.hideFocus = true;
35244         el.unselectable = "on";
35245         el.href = this.href || "#";
35246         if(this.hrefTarget){
35247             el.target = this.hrefTarget;
35248         }
35249         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35250         
35251         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35252         
35253         el.innerHTML = String.format(
35254                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35255                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35256         this.el = el;
35257         Roo.menu.Item.superclass.onRender.call(this, container, position);
35258     },
35259
35260     /**
35261      * Sets the text to display in this menu item
35262      * @param {String} text The text to display
35263      * @param {Boolean} isHTML true to indicate text is pure html.
35264      */
35265     setText : function(text, isHTML){
35266         if (isHTML) {
35267             this.html = text;
35268         } else {
35269             this.text = text;
35270             this.html = '';
35271         }
35272         if(this.rendered){
35273             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35274      
35275             this.el.update(String.format(
35276                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35277                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35278             this.parentMenu.autoWidth();
35279         }
35280     },
35281
35282     // private
35283     handleClick : function(e){
35284         if(!this.href){ // if no link defined, stop the event automatically
35285             e.stopEvent();
35286         }
35287         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35288     },
35289
35290     // private
35291     activate : function(autoExpand){
35292         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35293             this.focus();
35294             if(autoExpand){
35295                 this.expandMenu();
35296             }
35297         }
35298         return true;
35299     },
35300
35301     // private
35302     shouldDeactivate : function(e){
35303         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35304             if(this.menu && this.menu.isVisible()){
35305                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35306             }
35307             return true;
35308         }
35309         return false;
35310     },
35311
35312     // private
35313     deactivate : function(){
35314         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35315         this.hideMenu();
35316     },
35317
35318     // private
35319     expandMenu : function(autoActivate){
35320         if(!this.disabled && this.menu){
35321             clearTimeout(this.hideTimer);
35322             delete this.hideTimer;
35323             if(!this.menu.isVisible() && !this.showTimer){
35324                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35325             }else if (this.menu.isVisible() && autoActivate){
35326                 this.menu.tryActivate(0, 1);
35327             }
35328         }
35329     },
35330
35331     // private
35332     deferExpand : function(autoActivate){
35333         delete this.showTimer;
35334         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35335         if(autoActivate){
35336             this.menu.tryActivate(0, 1);
35337         }
35338     },
35339
35340     // private
35341     hideMenu : function(){
35342         clearTimeout(this.showTimer);
35343         delete this.showTimer;
35344         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35345             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35346         }
35347     },
35348
35349     // private
35350     deferHide : function(){
35351         delete this.hideTimer;
35352         this.menu.hide();
35353     }
35354 });/*
35355  * Based on:
35356  * Ext JS Library 1.1.1
35357  * Copyright(c) 2006-2007, Ext JS, LLC.
35358  *
35359  * Originally Released Under LGPL - original licence link has changed is not relivant.
35360  *
35361  * Fork - LGPL
35362  * <script type="text/javascript">
35363  */
35364  
35365 /**
35366  * @class Roo.menu.CheckItem
35367  * @extends Roo.menu.Item
35368  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35369  * @constructor
35370  * Creates a new CheckItem
35371  * @param {Object} config Configuration options
35372  */
35373 Roo.menu.CheckItem = function(config){
35374     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35375     this.addEvents({
35376         /**
35377          * @event beforecheckchange
35378          * Fires before the checked value is set, providing an opportunity to cancel if needed
35379          * @param {Roo.menu.CheckItem} this
35380          * @param {Boolean} checked The new checked value that will be set
35381          */
35382         "beforecheckchange" : true,
35383         /**
35384          * @event checkchange
35385          * Fires after the checked value has been set
35386          * @param {Roo.menu.CheckItem} this
35387          * @param {Boolean} checked The checked value that was set
35388          */
35389         "checkchange" : true
35390     });
35391     if(this.checkHandler){
35392         this.on('checkchange', this.checkHandler, this.scope);
35393     }
35394 };
35395 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35396     /**
35397      * @cfg {String} group
35398      * All check items with the same group name will automatically be grouped into a single-select
35399      * radio button group (defaults to '')
35400      */
35401     /**
35402      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35403      */
35404     itemCls : "x-menu-item x-menu-check-item",
35405     /**
35406      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35407      */
35408     groupClass : "x-menu-group-item",
35409
35410     /**
35411      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35412      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35413      * initialized with checked = true will be rendered as checked.
35414      */
35415     checked: false,
35416
35417     // private
35418     ctype: "Roo.menu.CheckItem",
35419
35420     // private
35421     onRender : function(c){
35422         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35423         if(this.group){
35424             this.el.addClass(this.groupClass);
35425         }
35426         Roo.menu.MenuMgr.registerCheckable(this);
35427         if(this.checked){
35428             this.checked = false;
35429             this.setChecked(true, true);
35430         }
35431     },
35432
35433     // private
35434     destroy : function(){
35435         if(this.rendered){
35436             Roo.menu.MenuMgr.unregisterCheckable(this);
35437         }
35438         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35439     },
35440
35441     /**
35442      * Set the checked state of this item
35443      * @param {Boolean} checked The new checked value
35444      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35445      */
35446     setChecked : function(state, suppressEvent){
35447         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35448             if(this.container){
35449                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35450             }
35451             this.checked = state;
35452             if(suppressEvent !== true){
35453                 this.fireEvent("checkchange", this, state);
35454             }
35455         }
35456     },
35457
35458     // private
35459     handleClick : function(e){
35460        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35461            this.setChecked(!this.checked);
35462        }
35463        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35464     }
35465 });/*
35466  * Based on:
35467  * Ext JS Library 1.1.1
35468  * Copyright(c) 2006-2007, Ext JS, LLC.
35469  *
35470  * Originally Released Under LGPL - original licence link has changed is not relivant.
35471  *
35472  * Fork - LGPL
35473  * <script type="text/javascript">
35474  */
35475  
35476 /**
35477  * @class Roo.menu.DateItem
35478  * @extends Roo.menu.Adapter
35479  * A menu item that wraps the {@link Roo.DatPicker} component.
35480  * @constructor
35481  * Creates a new DateItem
35482  * @param {Object} config Configuration options
35483  */
35484 Roo.menu.DateItem = function(config){
35485     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35486     /** The Roo.DatePicker object @type Roo.DatePicker */
35487     this.picker = this.component;
35488     this.addEvents({select: true});
35489     
35490     this.picker.on("render", function(picker){
35491         picker.getEl().swallowEvent("click");
35492         picker.container.addClass("x-menu-date-item");
35493     });
35494
35495     this.picker.on("select", this.onSelect, this);
35496 };
35497
35498 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35499     // private
35500     onSelect : function(picker, date){
35501         this.fireEvent("select", this, date, picker);
35502         Roo.menu.DateItem.superclass.handleClick.call(this);
35503     }
35504 });/*
35505  * Based on:
35506  * Ext JS Library 1.1.1
35507  * Copyright(c) 2006-2007, Ext JS, LLC.
35508  *
35509  * Originally Released Under LGPL - original licence link has changed is not relivant.
35510  *
35511  * Fork - LGPL
35512  * <script type="text/javascript">
35513  */
35514  
35515 /**
35516  * @class Roo.menu.ColorItem
35517  * @extends Roo.menu.Adapter
35518  * A menu item that wraps the {@link Roo.ColorPalette} component.
35519  * @constructor
35520  * Creates a new ColorItem
35521  * @param {Object} config Configuration options
35522  */
35523 Roo.menu.ColorItem = function(config){
35524     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35525     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35526     this.palette = this.component;
35527     this.relayEvents(this.palette, ["select"]);
35528     if(this.selectHandler){
35529         this.on('select', this.selectHandler, this.scope);
35530     }
35531 };
35532 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35533  * Based on:
35534  * Ext JS Library 1.1.1
35535  * Copyright(c) 2006-2007, Ext JS, LLC.
35536  *
35537  * Originally Released Under LGPL - original licence link has changed is not relivant.
35538  *
35539  * Fork - LGPL
35540  * <script type="text/javascript">
35541  */
35542  
35543
35544 /**
35545  * @class Roo.menu.DateMenu
35546  * @extends Roo.menu.Menu
35547  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35548  * @constructor
35549  * Creates a new DateMenu
35550  * @param {Object} config Configuration options
35551  */
35552 Roo.menu.DateMenu = function(config){
35553     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35554     this.plain = true;
35555     var di = new Roo.menu.DateItem(config);
35556     this.add(di);
35557     /**
35558      * The {@link Roo.DatePicker} instance for this DateMenu
35559      * @type DatePicker
35560      */
35561     this.picker = di.picker;
35562     /**
35563      * @event select
35564      * @param {DatePicker} picker
35565      * @param {Date} date
35566      */
35567     this.relayEvents(di, ["select"]);
35568
35569     this.on('beforeshow', function(){
35570         if(this.picker){
35571             this.picker.hideMonthPicker(true);
35572         }
35573     }, this);
35574 };
35575 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35576     cls:'x-date-menu'
35577 });/*
35578  * Based on:
35579  * Ext JS Library 1.1.1
35580  * Copyright(c) 2006-2007, Ext JS, LLC.
35581  *
35582  * Originally Released Under LGPL - original licence link has changed is not relivant.
35583  *
35584  * Fork - LGPL
35585  * <script type="text/javascript">
35586  */
35587  
35588
35589 /**
35590  * @class Roo.menu.ColorMenu
35591  * @extends Roo.menu.Menu
35592  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35593  * @constructor
35594  * Creates a new ColorMenu
35595  * @param {Object} config Configuration options
35596  */
35597 Roo.menu.ColorMenu = function(config){
35598     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35599     this.plain = true;
35600     var ci = new Roo.menu.ColorItem(config);
35601     this.add(ci);
35602     /**
35603      * The {@link Roo.ColorPalette} instance for this ColorMenu
35604      * @type ColorPalette
35605      */
35606     this.palette = ci.palette;
35607     /**
35608      * @event select
35609      * @param {ColorPalette} palette
35610      * @param {String} color
35611      */
35612     this.relayEvents(ci, ["select"]);
35613 };
35614 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35615  * Based on:
35616  * Ext JS Library 1.1.1
35617  * Copyright(c) 2006-2007, Ext JS, LLC.
35618  *
35619  * Originally Released Under LGPL - original licence link has changed is not relivant.
35620  *
35621  * Fork - LGPL
35622  * <script type="text/javascript">
35623  */
35624  
35625 /**
35626  * @class Roo.form.Field
35627  * @extends Roo.BoxComponent
35628  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35629  * @constructor
35630  * Creates a new Field
35631  * @param {Object} config Configuration options
35632  */
35633 Roo.form.Field = function(config){
35634     Roo.form.Field.superclass.constructor.call(this, config);
35635 };
35636
35637 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35638     /**
35639      * @cfg {String} fieldLabel Label to use when rendering a form.
35640      */
35641        /**
35642      * @cfg {String} qtip Mouse over tip
35643      */
35644      
35645     /**
35646      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35647      */
35648     invalidClass : "x-form-invalid",
35649     /**
35650      * @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")
35651      */
35652     invalidText : "The value in this field is invalid",
35653     /**
35654      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35655      */
35656     focusClass : "x-form-focus",
35657     /**
35658      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35659       automatic validation (defaults to "keyup").
35660      */
35661     validationEvent : "keyup",
35662     /**
35663      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35664      */
35665     validateOnBlur : true,
35666     /**
35667      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35668      */
35669     validationDelay : 250,
35670     /**
35671      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35672      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35673      */
35674     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35675     /**
35676      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35677      */
35678     fieldClass : "x-form-field",
35679     /**
35680      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35681      *<pre>
35682 Value         Description
35683 -----------   ----------------------------------------------------------------------
35684 qtip          Display a quick tip when the user hovers over the field
35685 title         Display a default browser title attribute popup
35686 under         Add a block div beneath the field containing the error text
35687 side          Add an error icon to the right of the field with a popup on hover
35688 [element id]  Add the error text directly to the innerHTML of the specified element
35689 </pre>
35690      */
35691     msgTarget : 'qtip',
35692     /**
35693      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35694      */
35695     msgFx : 'normal',
35696
35697     /**
35698      * @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.
35699      */
35700     readOnly : false,
35701
35702     /**
35703      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35704      */
35705     disabled : false,
35706
35707     /**
35708      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35709      */
35710     inputType : undefined,
35711     
35712     /**
35713      * @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).
35714          */
35715         tabIndex : undefined,
35716         
35717     // private
35718     isFormField : true,
35719
35720     // private
35721     hasFocus : false,
35722     /**
35723      * @property {Roo.Element} fieldEl
35724      * Element Containing the rendered Field (with label etc.)
35725      */
35726     /**
35727      * @cfg {Mixed} value A value to initialize this field with.
35728      */
35729     value : undefined,
35730
35731     /**
35732      * @cfg {String} name The field's HTML name attribute.
35733      */
35734     /**
35735      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35736      */
35737
35738         // private ??
35739         initComponent : function(){
35740         Roo.form.Field.superclass.initComponent.call(this);
35741         this.addEvents({
35742             /**
35743              * @event focus
35744              * Fires when this field receives input focus.
35745              * @param {Roo.form.Field} this
35746              */
35747             focus : true,
35748             /**
35749              * @event blur
35750              * Fires when this field loses input focus.
35751              * @param {Roo.form.Field} this
35752              */
35753             blur : true,
35754             /**
35755              * @event specialkey
35756              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35757              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35758              * @param {Roo.form.Field} this
35759              * @param {Roo.EventObject} e The event object
35760              */
35761             specialkey : true,
35762             /**
35763              * @event change
35764              * Fires just before the field blurs if the field value has changed.
35765              * @param {Roo.form.Field} this
35766              * @param {Mixed} newValue The new value
35767              * @param {Mixed} oldValue The original value
35768              */
35769             change : true,
35770             /**
35771              * @event invalid
35772              * Fires after the field has been marked as invalid.
35773              * @param {Roo.form.Field} this
35774              * @param {String} msg The validation message
35775              */
35776             invalid : true,
35777             /**
35778              * @event valid
35779              * Fires after the field has been validated with no errors.
35780              * @param {Roo.form.Field} this
35781              */
35782             valid : true,
35783              /**
35784              * @event keyup
35785              * Fires after the key up
35786              * @param {Roo.form.Field} this
35787              * @param {Roo.EventObject}  e The event Object
35788              */
35789             keyup : true
35790         });
35791     },
35792
35793     /**
35794      * Returns the name attribute of the field if available
35795      * @return {String} name The field name
35796      */
35797     getName: function(){
35798          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35799     },
35800
35801     // private
35802     onRender : function(ct, position){
35803         Roo.form.Field.superclass.onRender.call(this, ct, position);
35804         if(!this.el){
35805             var cfg = this.getAutoCreate();
35806             if(!cfg.name){
35807                 cfg.name = this.name || this.id;
35808             }
35809             if(this.inputType){
35810                 cfg.type = this.inputType;
35811             }
35812             this.el = ct.createChild(cfg, position);
35813         }
35814         var type = this.el.dom.type;
35815         if(type){
35816             if(type == 'password'){
35817                 type = 'text';
35818             }
35819             this.el.addClass('x-form-'+type);
35820         }
35821         if(this.readOnly){
35822             this.el.dom.readOnly = true;
35823         }
35824         if(this.tabIndex !== undefined){
35825             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35826         }
35827
35828         this.el.addClass([this.fieldClass, this.cls]);
35829         this.initValue();
35830     },
35831
35832     /**
35833      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35834      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35835      * @return {Roo.form.Field} this
35836      */
35837     applyTo : function(target){
35838         this.allowDomMove = false;
35839         this.el = Roo.get(target);
35840         this.render(this.el.dom.parentNode);
35841         return this;
35842     },
35843
35844     // private
35845     initValue : function(){
35846         if(this.value !== undefined){
35847             this.setValue(this.value);
35848         }else if(this.el.dom.value.length > 0){
35849             this.setValue(this.el.dom.value);
35850         }
35851     },
35852
35853     /**
35854      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35855      */
35856     isDirty : function() {
35857         if(this.disabled) {
35858             return false;
35859         }
35860         return String(this.getValue()) !== String(this.originalValue);
35861     },
35862
35863     // private
35864     afterRender : function(){
35865         Roo.form.Field.superclass.afterRender.call(this);
35866         this.initEvents();
35867     },
35868
35869     // private
35870     fireKey : function(e){
35871         //Roo.log('field ' + e.getKey());
35872         if(e.isNavKeyPress()){
35873             this.fireEvent("specialkey", this, e);
35874         }
35875     },
35876
35877     /**
35878      * Resets the current field value to the originally loaded value and clears any validation messages
35879      */
35880     reset : function(){
35881         this.setValue(this.originalValue);
35882         this.clearInvalid();
35883     },
35884
35885     // private
35886     initEvents : function(){
35887         // safari killled keypress - so keydown is now used..
35888         this.el.on("keydown" , this.fireKey,  this);
35889         this.el.on("focus", this.onFocus,  this);
35890         this.el.on("blur", this.onBlur,  this);
35891         this.el.relayEvent('keyup', this);
35892
35893         // reference to original value for reset
35894         this.originalValue = this.getValue();
35895     },
35896
35897     // private
35898     onFocus : function(){
35899         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35900             this.el.addClass(this.focusClass);
35901         }
35902         if(!this.hasFocus){
35903             this.hasFocus = true;
35904             this.startValue = this.getValue();
35905             this.fireEvent("focus", this);
35906         }
35907     },
35908
35909     beforeBlur : Roo.emptyFn,
35910
35911     // private
35912     onBlur : function(){
35913         this.beforeBlur();
35914         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35915             this.el.removeClass(this.focusClass);
35916         }
35917         this.hasFocus = false;
35918         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35919             this.validate();
35920         }
35921         var v = this.getValue();
35922         if(String(v) !== String(this.startValue)){
35923             this.fireEvent('change', this, v, this.startValue);
35924         }
35925         this.fireEvent("blur", this);
35926     },
35927
35928     /**
35929      * Returns whether or not the field value is currently valid
35930      * @param {Boolean} preventMark True to disable marking the field invalid
35931      * @return {Boolean} True if the value is valid, else false
35932      */
35933     isValid : function(preventMark){
35934         if(this.disabled){
35935             return true;
35936         }
35937         var restore = this.preventMark;
35938         this.preventMark = preventMark === true;
35939         var v = this.validateValue(this.processValue(this.getRawValue()));
35940         this.preventMark = restore;
35941         return v;
35942     },
35943
35944     /**
35945      * Validates the field value
35946      * @return {Boolean} True if the value is valid, else false
35947      */
35948     validate : function(){
35949         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35950             this.clearInvalid();
35951             return true;
35952         }
35953         return false;
35954     },
35955
35956     processValue : function(value){
35957         return value;
35958     },
35959
35960     // private
35961     // Subclasses should provide the validation implementation by overriding this
35962     validateValue : function(value){
35963         return true;
35964     },
35965
35966     /**
35967      * Mark this field as invalid
35968      * @param {String} msg The validation message
35969      */
35970     markInvalid : function(msg){
35971         if(!this.rendered || this.preventMark){ // not rendered
35972             return;
35973         }
35974         this.el.addClass(this.invalidClass);
35975         msg = msg || this.invalidText;
35976         switch(this.msgTarget){
35977             case 'qtip':
35978                 this.el.dom.qtip = msg;
35979                 this.el.dom.qclass = 'x-form-invalid-tip';
35980                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35981                     Roo.QuickTips.enable();
35982                 }
35983                 break;
35984             case 'title':
35985                 this.el.dom.title = msg;
35986                 break;
35987             case 'under':
35988                 if(!this.errorEl){
35989                     var elp = this.el.findParent('.x-form-element', 5, true);
35990                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35991                     this.errorEl.setWidth(elp.getWidth(true)-20);
35992                 }
35993                 this.errorEl.update(msg);
35994                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35995                 break;
35996             case 'side':
35997                 if(!this.errorIcon){
35998                     var elp = this.el.findParent('.x-form-element', 5, true);
35999                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36000                 }
36001                 this.alignErrorIcon();
36002                 this.errorIcon.dom.qtip = msg;
36003                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36004                 this.errorIcon.show();
36005                 this.on('resize', this.alignErrorIcon, this);
36006                 break;
36007             default:
36008                 var t = Roo.getDom(this.msgTarget);
36009                 t.innerHTML = msg;
36010                 t.style.display = this.msgDisplay;
36011                 break;
36012         }
36013         this.fireEvent('invalid', this, msg);
36014     },
36015
36016     // private
36017     alignErrorIcon : function(){
36018         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36019     },
36020
36021     /**
36022      * Clear any invalid styles/messages for this field
36023      */
36024     clearInvalid : function(){
36025         if(!this.rendered || this.preventMark){ // not rendered
36026             return;
36027         }
36028         this.el.removeClass(this.invalidClass);
36029         switch(this.msgTarget){
36030             case 'qtip':
36031                 this.el.dom.qtip = '';
36032                 break;
36033             case 'title':
36034                 this.el.dom.title = '';
36035                 break;
36036             case 'under':
36037                 if(this.errorEl){
36038                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36039                 }
36040                 break;
36041             case 'side':
36042                 if(this.errorIcon){
36043                     this.errorIcon.dom.qtip = '';
36044                     this.errorIcon.hide();
36045                     this.un('resize', this.alignErrorIcon, this);
36046                 }
36047                 break;
36048             default:
36049                 var t = Roo.getDom(this.msgTarget);
36050                 t.innerHTML = '';
36051                 t.style.display = 'none';
36052                 break;
36053         }
36054         this.fireEvent('valid', this);
36055     },
36056
36057     /**
36058      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36059      * @return {Mixed} value The field value
36060      */
36061     getRawValue : function(){
36062         var v = this.el.getValue();
36063         if(v === this.emptyText){
36064             v = '';
36065         }
36066         return v;
36067     },
36068
36069     /**
36070      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36071      * @return {Mixed} value The field value
36072      */
36073     getValue : function(){
36074         var v = this.el.getValue();
36075         if(v === this.emptyText || v === undefined){
36076             v = '';
36077         }
36078         return v;
36079     },
36080
36081     /**
36082      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36083      * @param {Mixed} value The value to set
36084      */
36085     setRawValue : function(v){
36086         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36087     },
36088
36089     /**
36090      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36091      * @param {Mixed} value The value to set
36092      */
36093     setValue : function(v){
36094         this.value = v;
36095         if(this.rendered){
36096             this.el.dom.value = (v === null || v === undefined ? '' : v);
36097              this.validate();
36098         }
36099     },
36100
36101     adjustSize : function(w, h){
36102         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36103         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36104         return s;
36105     },
36106
36107     adjustWidth : function(tag, w){
36108         tag = tag.toLowerCase();
36109         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36110             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36111                 if(tag == 'input'){
36112                     return w + 2;
36113                 }
36114                 if(tag = 'textarea'){
36115                     return w-2;
36116                 }
36117             }else if(Roo.isOpera){
36118                 if(tag == 'input'){
36119                     return w + 2;
36120                 }
36121                 if(tag = 'textarea'){
36122                     return w-2;
36123                 }
36124             }
36125         }
36126         return w;
36127     }
36128 });
36129
36130
36131 // anything other than normal should be considered experimental
36132 Roo.form.Field.msgFx = {
36133     normal : {
36134         show: function(msgEl, f){
36135             msgEl.setDisplayed('block');
36136         },
36137
36138         hide : function(msgEl, f){
36139             msgEl.setDisplayed(false).update('');
36140         }
36141     },
36142
36143     slide : {
36144         show: function(msgEl, f){
36145             msgEl.slideIn('t', {stopFx:true});
36146         },
36147
36148         hide : function(msgEl, f){
36149             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36150         }
36151     },
36152
36153     slideRight : {
36154         show: function(msgEl, f){
36155             msgEl.fixDisplay();
36156             msgEl.alignTo(f.el, 'tl-tr');
36157             msgEl.slideIn('l', {stopFx:true});
36158         },
36159
36160         hide : function(msgEl, f){
36161             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36162         }
36163     }
36164 };/*
36165  * Based on:
36166  * Ext JS Library 1.1.1
36167  * Copyright(c) 2006-2007, Ext JS, LLC.
36168  *
36169  * Originally Released Under LGPL - original licence link has changed is not relivant.
36170  *
36171  * Fork - LGPL
36172  * <script type="text/javascript">
36173  */
36174  
36175
36176 /**
36177  * @class Roo.form.TextField
36178  * @extends Roo.form.Field
36179  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36180  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36181  * @constructor
36182  * Creates a new TextField
36183  * @param {Object} config Configuration options
36184  */
36185 Roo.form.TextField = function(config){
36186     Roo.form.TextField.superclass.constructor.call(this, config);
36187     this.addEvents({
36188         /**
36189          * @event autosize
36190          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36191          * according to the default logic, but this event provides a hook for the developer to apply additional
36192          * logic at runtime to resize the field if needed.
36193              * @param {Roo.form.Field} this This text field
36194              * @param {Number} width The new field width
36195              */
36196         autosize : true
36197     });
36198 };
36199
36200 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36201     /**
36202      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36203      */
36204     grow : false,
36205     /**
36206      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36207      */
36208     growMin : 30,
36209     /**
36210      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36211      */
36212     growMax : 800,
36213     /**
36214      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36215      */
36216     vtype : null,
36217     /**
36218      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36219      */
36220     maskRe : null,
36221     /**
36222      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36223      */
36224     disableKeyFilter : false,
36225     /**
36226      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36227      */
36228     allowBlank : true,
36229     /**
36230      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36231      */
36232     minLength : 0,
36233     /**
36234      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36235      */
36236     maxLength : Number.MAX_VALUE,
36237     /**
36238      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36239      */
36240     minLengthText : "The minimum length for this field is {0}",
36241     /**
36242      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36243      */
36244     maxLengthText : "The maximum length for this field is {0}",
36245     /**
36246      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36247      */
36248     selectOnFocus : false,
36249     /**
36250      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36251      */
36252     blankText : "This field is required",
36253     /**
36254      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36255      * If available, this function will be called only after the basic validators all return true, and will be passed the
36256      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36257      */
36258     validator : null,
36259     /**
36260      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36261      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36262      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36263      */
36264     regex : null,
36265     /**
36266      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36267      */
36268     regexText : "",
36269     /**
36270      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36271      */
36272     emptyText : null,
36273     /**
36274      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36275      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36276      */
36277     emptyClass : 'x-form-empty-field',
36278
36279     // private
36280     initEvents : function(){
36281         Roo.form.TextField.superclass.initEvents.call(this);
36282         if(this.validationEvent == 'keyup'){
36283             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36284             this.el.on('keyup', this.filterValidation, this);
36285         }
36286         else if(this.validationEvent !== false){
36287             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36288         }
36289         if(this.selectOnFocus || this.emptyText){
36290             this.on("focus", this.preFocus, this);
36291             if(this.emptyText){
36292                 this.on('blur', this.postBlur, this);
36293                 this.applyEmptyText();
36294             }
36295         }
36296         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36297             this.el.on("keypress", this.filterKeys, this);
36298         }
36299         if(this.grow){
36300             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36301             this.el.on("click", this.autoSize,  this);
36302         }
36303     },
36304
36305     processValue : function(value){
36306         if(this.stripCharsRe){
36307             var newValue = value.replace(this.stripCharsRe, '');
36308             if(newValue !== value){
36309                 this.setRawValue(newValue);
36310                 return newValue;
36311             }
36312         }
36313         return value;
36314     },
36315
36316     filterValidation : function(e){
36317         if(!e.isNavKeyPress()){
36318             this.validationTask.delay(this.validationDelay);
36319         }
36320     },
36321
36322     // private
36323     onKeyUp : function(e){
36324         if(!e.isNavKeyPress()){
36325             this.autoSize();
36326         }
36327     },
36328
36329     /**
36330      * Resets the current field value to the originally-loaded value and clears any validation messages.
36331      * Also adds emptyText and emptyClass if the original value was blank.
36332      */
36333     reset : function(){
36334         Roo.form.TextField.superclass.reset.call(this);
36335         this.applyEmptyText();
36336     },
36337
36338     applyEmptyText : function(){
36339         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36340             this.setRawValue(this.emptyText);
36341             this.el.addClass(this.emptyClass);
36342         }
36343     },
36344
36345     // private
36346     preFocus : function(){
36347         if(this.emptyText){
36348             if(this.el.dom.value == this.emptyText){
36349                 this.setRawValue('');
36350             }
36351             this.el.removeClass(this.emptyClass);
36352         }
36353         if(this.selectOnFocus){
36354             this.el.dom.select();
36355         }
36356     },
36357
36358     // private
36359     postBlur : function(){
36360         this.applyEmptyText();
36361     },
36362
36363     // private
36364     filterKeys : function(e){
36365         var k = e.getKey();
36366         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36367             return;
36368         }
36369         var c = e.getCharCode(), cc = String.fromCharCode(c);
36370         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36371             return;
36372         }
36373         if(!this.maskRe.test(cc)){
36374             e.stopEvent();
36375         }
36376     },
36377
36378     setValue : function(v){
36379         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36380             this.el.removeClass(this.emptyClass);
36381         }
36382         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36383         this.applyEmptyText();
36384         this.autoSize();
36385     },
36386
36387     /**
36388      * Validates a value according to the field's validation rules and marks the field as invalid
36389      * if the validation fails
36390      * @param {Mixed} value The value to validate
36391      * @return {Boolean} True if the value is valid, else false
36392      */
36393     validateValue : function(value){
36394         if(value.length < 1 || value === this.emptyText){ // if it's blank
36395              if(this.allowBlank){
36396                 this.clearInvalid();
36397                 return true;
36398              }else{
36399                 this.markInvalid(this.blankText);
36400                 return false;
36401              }
36402         }
36403         if(value.length < this.minLength){
36404             this.markInvalid(String.format(this.minLengthText, this.minLength));
36405             return false;
36406         }
36407         if(value.length > this.maxLength){
36408             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36409             return false;
36410         }
36411         if(this.vtype){
36412             var vt = Roo.form.VTypes;
36413             if(!vt[this.vtype](value, this)){
36414                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36415                 return false;
36416             }
36417         }
36418         if(typeof this.validator == "function"){
36419             var msg = this.validator(value);
36420             if(msg !== true){
36421                 this.markInvalid(msg);
36422                 return false;
36423             }
36424         }
36425         if(this.regex && !this.regex.test(value)){
36426             this.markInvalid(this.regexText);
36427             return false;
36428         }
36429         return true;
36430     },
36431
36432     /**
36433      * Selects text in this field
36434      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36435      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36436      */
36437     selectText : function(start, end){
36438         var v = this.getRawValue();
36439         if(v.length > 0){
36440             start = start === undefined ? 0 : start;
36441             end = end === undefined ? v.length : end;
36442             var d = this.el.dom;
36443             if(d.setSelectionRange){
36444                 d.setSelectionRange(start, end);
36445             }else if(d.createTextRange){
36446                 var range = d.createTextRange();
36447                 range.moveStart("character", start);
36448                 range.moveEnd("character", v.length-end);
36449                 range.select();
36450             }
36451         }
36452     },
36453
36454     /**
36455      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36456      * This only takes effect if grow = true, and fires the autosize event.
36457      */
36458     autoSize : function(){
36459         if(!this.grow || !this.rendered){
36460             return;
36461         }
36462         if(!this.metrics){
36463             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36464         }
36465         var el = this.el;
36466         var v = el.dom.value;
36467         var d = document.createElement('div');
36468         d.appendChild(document.createTextNode(v));
36469         v = d.innerHTML;
36470         d = null;
36471         v += "&#160;";
36472         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36473         this.el.setWidth(w);
36474         this.fireEvent("autosize", this, w);
36475     }
36476 });/*
36477  * Based on:
36478  * Ext JS Library 1.1.1
36479  * Copyright(c) 2006-2007, Ext JS, LLC.
36480  *
36481  * Originally Released Under LGPL - original licence link has changed is not relivant.
36482  *
36483  * Fork - LGPL
36484  * <script type="text/javascript">
36485  */
36486  
36487 /**
36488  * @class Roo.form.Hidden
36489  * @extends Roo.form.TextField
36490  * Simple Hidden element used on forms 
36491  * 
36492  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36493  * 
36494  * @constructor
36495  * Creates a new Hidden form element.
36496  * @param {Object} config Configuration options
36497  */
36498
36499
36500
36501 // easy hidden field...
36502 Roo.form.Hidden = function(config){
36503     Roo.form.Hidden.superclass.constructor.call(this, config);
36504 };
36505   
36506 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36507     fieldLabel:      '',
36508     inputType:      'hidden',
36509     width:          50,
36510     allowBlank:     true,
36511     labelSeparator: '',
36512     hidden:         true,
36513     itemCls :       'x-form-item-display-none'
36514
36515
36516 });
36517
36518
36519 /*
36520  * Based on:
36521  * Ext JS Library 1.1.1
36522  * Copyright(c) 2006-2007, Ext JS, LLC.
36523  *
36524  * Originally Released Under LGPL - original licence link has changed is not relivant.
36525  *
36526  * Fork - LGPL
36527  * <script type="text/javascript">
36528  */
36529  
36530 /**
36531  * @class Roo.form.TriggerField
36532  * @extends Roo.form.TextField
36533  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36534  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36535  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36536  * for which you can provide a custom implementation.  For example:
36537  * <pre><code>
36538 var trigger = new Roo.form.TriggerField();
36539 trigger.onTriggerClick = myTriggerFn;
36540 trigger.applyTo('my-field');
36541 </code></pre>
36542  *
36543  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36544  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36545  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36546  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36547  * @constructor
36548  * Create a new TriggerField.
36549  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36550  * to the base TextField)
36551  */
36552 Roo.form.TriggerField = function(config){
36553     this.mimicing = false;
36554     Roo.form.TriggerField.superclass.constructor.call(this, config);
36555 };
36556
36557 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36558     /**
36559      * @cfg {String} triggerClass A CSS class to apply to the trigger
36560      */
36561     /**
36562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36563      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36564      */
36565     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36566     /**
36567      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36568      */
36569     hideTrigger:false,
36570
36571     /** @cfg {Boolean} grow @hide */
36572     /** @cfg {Number} growMin @hide */
36573     /** @cfg {Number} growMax @hide */
36574
36575     /**
36576      * @hide 
36577      * @method
36578      */
36579     autoSize: Roo.emptyFn,
36580     // private
36581     monitorTab : true,
36582     // private
36583     deferHeight : true,
36584
36585     
36586     actionMode : 'wrap',
36587     // private
36588     onResize : function(w, h){
36589         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36590         if(typeof w == 'number'){
36591             var x = w - this.trigger.getWidth();
36592             this.el.setWidth(this.adjustWidth('input', x));
36593             this.trigger.setStyle('left', x+'px');
36594         }
36595     },
36596
36597     // private
36598     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36599
36600     // private
36601     getResizeEl : function(){
36602         return this.wrap;
36603     },
36604
36605     // private
36606     getPositionEl : function(){
36607         return this.wrap;
36608     },
36609
36610     // private
36611     alignErrorIcon : function(){
36612         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36613     },
36614
36615     // private
36616     onRender : function(ct, position){
36617         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36618         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36619         this.trigger = this.wrap.createChild(this.triggerConfig ||
36620                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36621         if(this.hideTrigger){
36622             this.trigger.setDisplayed(false);
36623         }
36624         this.initTrigger();
36625         if(!this.width){
36626             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36627         }
36628     },
36629
36630     // private
36631     initTrigger : function(){
36632         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36633         this.trigger.addClassOnOver('x-form-trigger-over');
36634         this.trigger.addClassOnClick('x-form-trigger-click');
36635     },
36636
36637     // private
36638     onDestroy : function(){
36639         if(this.trigger){
36640             this.trigger.removeAllListeners();
36641             this.trigger.remove();
36642         }
36643         if(this.wrap){
36644             this.wrap.remove();
36645         }
36646         Roo.form.TriggerField.superclass.onDestroy.call(this);
36647     },
36648
36649     // private
36650     onFocus : function(){
36651         Roo.form.TriggerField.superclass.onFocus.call(this);
36652         if(!this.mimicing){
36653             this.wrap.addClass('x-trigger-wrap-focus');
36654             this.mimicing = true;
36655             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36656             if(this.monitorTab){
36657                 this.el.on("keydown", this.checkTab, this);
36658             }
36659         }
36660     },
36661
36662     // private
36663     checkTab : function(e){
36664         if(e.getKey() == e.TAB){
36665             this.triggerBlur();
36666         }
36667     },
36668
36669     // private
36670     onBlur : function(){
36671         // do nothing
36672     },
36673
36674     // private
36675     mimicBlur : function(e, t){
36676         if(!this.wrap.contains(t) && this.validateBlur()){
36677             this.triggerBlur();
36678         }
36679     },
36680
36681     // private
36682     triggerBlur : function(){
36683         this.mimicing = false;
36684         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36685         if(this.monitorTab){
36686             this.el.un("keydown", this.checkTab, this);
36687         }
36688         this.wrap.removeClass('x-trigger-wrap-focus');
36689         Roo.form.TriggerField.superclass.onBlur.call(this);
36690     },
36691
36692     // private
36693     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36694     validateBlur : function(e, t){
36695         return true;
36696     },
36697
36698     // private
36699     onDisable : function(){
36700         Roo.form.TriggerField.superclass.onDisable.call(this);
36701         if(this.wrap){
36702             this.wrap.addClass('x-item-disabled');
36703         }
36704     },
36705
36706     // private
36707     onEnable : function(){
36708         Roo.form.TriggerField.superclass.onEnable.call(this);
36709         if(this.wrap){
36710             this.wrap.removeClass('x-item-disabled');
36711         }
36712     },
36713
36714     // private
36715     onShow : function(){
36716         var ae = this.getActionEl();
36717         
36718         if(ae){
36719             ae.dom.style.display = '';
36720             ae.dom.style.visibility = 'visible';
36721         }
36722     },
36723
36724     // private
36725     
36726     onHide : function(){
36727         var ae = this.getActionEl();
36728         ae.dom.style.display = 'none';
36729     },
36730
36731     /**
36732      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36733      * by an implementing function.
36734      * @method
36735      * @param {EventObject} e
36736      */
36737     onTriggerClick : Roo.emptyFn
36738 });
36739
36740 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36741 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36742 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36743 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36744     initComponent : function(){
36745         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36746
36747         this.triggerConfig = {
36748             tag:'span', cls:'x-form-twin-triggers', cn:[
36749             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36750             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36751         ]};
36752     },
36753
36754     getTrigger : function(index){
36755         return this.triggers[index];
36756     },
36757
36758     initTrigger : function(){
36759         var ts = this.trigger.select('.x-form-trigger', true);
36760         this.wrap.setStyle('overflow', 'hidden');
36761         var triggerField = this;
36762         ts.each(function(t, all, index){
36763             t.hide = function(){
36764                 var w = triggerField.wrap.getWidth();
36765                 this.dom.style.display = 'none';
36766                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36767             };
36768             t.show = function(){
36769                 var w = triggerField.wrap.getWidth();
36770                 this.dom.style.display = '';
36771                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36772             };
36773             var triggerIndex = 'Trigger'+(index+1);
36774
36775             if(this['hide'+triggerIndex]){
36776                 t.dom.style.display = 'none';
36777             }
36778             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36779             t.addClassOnOver('x-form-trigger-over');
36780             t.addClassOnClick('x-form-trigger-click');
36781         }, this);
36782         this.triggers = ts.elements;
36783     },
36784
36785     onTrigger1Click : Roo.emptyFn,
36786     onTrigger2Click : Roo.emptyFn
36787 });/*
36788  * Based on:
36789  * Ext JS Library 1.1.1
36790  * Copyright(c) 2006-2007, Ext JS, LLC.
36791  *
36792  * Originally Released Under LGPL - original licence link has changed is not relivant.
36793  *
36794  * Fork - LGPL
36795  * <script type="text/javascript">
36796  */
36797  
36798 /**
36799  * @class Roo.form.TextArea
36800  * @extends Roo.form.TextField
36801  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36802  * support for auto-sizing.
36803  * @constructor
36804  * Creates a new TextArea
36805  * @param {Object} config Configuration options
36806  */
36807 Roo.form.TextArea = function(config){
36808     Roo.form.TextArea.superclass.constructor.call(this, config);
36809     // these are provided exchanges for backwards compat
36810     // minHeight/maxHeight were replaced by growMin/growMax to be
36811     // compatible with TextField growing config values
36812     if(this.minHeight !== undefined){
36813         this.growMin = this.minHeight;
36814     }
36815     if(this.maxHeight !== undefined){
36816         this.growMax = this.maxHeight;
36817     }
36818 };
36819
36820 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36821     /**
36822      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36823      */
36824     growMin : 60,
36825     /**
36826      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36827      */
36828     growMax: 1000,
36829     /**
36830      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36831      * in the field (equivalent to setting overflow: hidden, defaults to false)
36832      */
36833     preventScrollbars: false,
36834     /**
36835      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36836      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36837      */
36838
36839     // private
36840     onRender : function(ct, position){
36841         if(!this.el){
36842             this.defaultAutoCreate = {
36843                 tag: "textarea",
36844                 style:"width:300px;height:60px;",
36845                 autocomplete: "off"
36846             };
36847         }
36848         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36849         if(this.grow){
36850             this.textSizeEl = Roo.DomHelper.append(document.body, {
36851                 tag: "pre", cls: "x-form-grow-sizer"
36852             });
36853             if(this.preventScrollbars){
36854                 this.el.setStyle("overflow", "hidden");
36855             }
36856             this.el.setHeight(this.growMin);
36857         }
36858     },
36859
36860     onDestroy : function(){
36861         if(this.textSizeEl){
36862             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36863         }
36864         Roo.form.TextArea.superclass.onDestroy.call(this);
36865     },
36866
36867     // private
36868     onKeyUp : function(e){
36869         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36870             this.autoSize();
36871         }
36872     },
36873
36874     /**
36875      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36876      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36877      */
36878     autoSize : function(){
36879         if(!this.grow || !this.textSizeEl){
36880             return;
36881         }
36882         var el = this.el;
36883         var v = el.dom.value;
36884         var ts = this.textSizeEl;
36885
36886         ts.innerHTML = '';
36887         ts.appendChild(document.createTextNode(v));
36888         v = ts.innerHTML;
36889
36890         Roo.fly(ts).setWidth(this.el.getWidth());
36891         if(v.length < 1){
36892             v = "&#160;&#160;";
36893         }else{
36894             if(Roo.isIE){
36895                 v = v.replace(/\n/g, '<p>&#160;</p>');
36896             }
36897             v += "&#160;\n&#160;";
36898         }
36899         ts.innerHTML = v;
36900         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36901         if(h != this.lastHeight){
36902             this.lastHeight = h;
36903             this.el.setHeight(h);
36904             this.fireEvent("autosize", this, h);
36905         }
36906     }
36907 });/*
36908  * Based on:
36909  * Ext JS Library 1.1.1
36910  * Copyright(c) 2006-2007, Ext JS, LLC.
36911  *
36912  * Originally Released Under LGPL - original licence link has changed is not relivant.
36913  *
36914  * Fork - LGPL
36915  * <script type="text/javascript">
36916  */
36917  
36918
36919 /**
36920  * @class Roo.form.NumberField
36921  * @extends Roo.form.TextField
36922  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36923  * @constructor
36924  * Creates a new NumberField
36925  * @param {Object} config Configuration options
36926  */
36927 Roo.form.NumberField = function(config){
36928     Roo.form.NumberField.superclass.constructor.call(this, config);
36929 };
36930
36931 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36932     /**
36933      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36934      */
36935     fieldClass: "x-form-field x-form-num-field",
36936     /**
36937      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36938      */
36939     allowDecimals : true,
36940     /**
36941      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36942      */
36943     decimalSeparator : ".",
36944     /**
36945      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36946      */
36947     decimalPrecision : 2,
36948     /**
36949      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36950      */
36951     allowNegative : true,
36952     /**
36953      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36954      */
36955     minValue : Number.NEGATIVE_INFINITY,
36956     /**
36957      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36958      */
36959     maxValue : Number.MAX_VALUE,
36960     /**
36961      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36962      */
36963     minText : "The minimum value for this field is {0}",
36964     /**
36965      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36966      */
36967     maxText : "The maximum value for this field is {0}",
36968     /**
36969      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36970      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36971      */
36972     nanText : "{0} is not a valid number",
36973
36974     // private
36975     initEvents : function(){
36976         Roo.form.NumberField.superclass.initEvents.call(this);
36977         var allowed = "0123456789";
36978         if(this.allowDecimals){
36979             allowed += this.decimalSeparator;
36980         }
36981         if(this.allowNegative){
36982             allowed += "-";
36983         }
36984         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36985         var keyPress = function(e){
36986             var k = e.getKey();
36987             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36988                 return;
36989             }
36990             var c = e.getCharCode();
36991             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36992                 e.stopEvent();
36993             }
36994         };
36995         this.el.on("keypress", keyPress, this);
36996     },
36997
36998     // private
36999     validateValue : function(value){
37000         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37001             return false;
37002         }
37003         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37004              return true;
37005         }
37006         var num = this.parseValue(value);
37007         if(isNaN(num)){
37008             this.markInvalid(String.format(this.nanText, value));
37009             return false;
37010         }
37011         if(num < this.minValue){
37012             this.markInvalid(String.format(this.minText, this.minValue));
37013             return false;
37014         }
37015         if(num > this.maxValue){
37016             this.markInvalid(String.format(this.maxText, this.maxValue));
37017             return false;
37018         }
37019         return true;
37020     },
37021
37022     getValue : function(){
37023         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37024     },
37025
37026     // private
37027     parseValue : function(value){
37028         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37029         return isNaN(value) ? '' : value;
37030     },
37031
37032     // private
37033     fixPrecision : function(value){
37034         var nan = isNaN(value);
37035         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37036             return nan ? '' : value;
37037         }
37038         return parseFloat(value).toFixed(this.decimalPrecision);
37039     },
37040
37041     setValue : function(v){
37042         v = this.fixPrecision(v);
37043         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37044     },
37045
37046     // private
37047     decimalPrecisionFcn : function(v){
37048         return Math.floor(v);
37049     },
37050
37051     beforeBlur : function(){
37052         var v = this.parseValue(this.getRawValue());
37053         if(v){
37054             this.setValue(v);
37055         }
37056     }
37057 });/*
37058  * Based on:
37059  * Ext JS Library 1.1.1
37060  * Copyright(c) 2006-2007, Ext JS, LLC.
37061  *
37062  * Originally Released Under LGPL - original licence link has changed is not relivant.
37063  *
37064  * Fork - LGPL
37065  * <script type="text/javascript">
37066  */
37067  
37068 /**
37069  * @class Roo.form.DateField
37070  * @extends Roo.form.TriggerField
37071  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37072 * @constructor
37073 * Create a new DateField
37074 * @param {Object} config
37075  */
37076 Roo.form.DateField = function(config){
37077     Roo.form.DateField.superclass.constructor.call(this, config);
37078     
37079       this.addEvents({
37080          
37081         /**
37082          * @event select
37083          * Fires when a date is selected
37084              * @param {Roo.form.DateField} combo This combo box
37085              * @param {Date} date The date selected
37086              */
37087         'select' : true
37088          
37089     });
37090     
37091     
37092     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37093     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37094     this.ddMatch = null;
37095     if(this.disabledDates){
37096         var dd = this.disabledDates;
37097         var re = "(?:";
37098         for(var i = 0; i < dd.length; i++){
37099             re += dd[i];
37100             if(i != dd.length-1) re += "|";
37101         }
37102         this.ddMatch = new RegExp(re + ")");
37103     }
37104 };
37105
37106 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37107     /**
37108      * @cfg {String} format
37109      * The default date format string which can be overriden for localization support.  The format must be
37110      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37111      */
37112     format : "m/d/y",
37113     /**
37114      * @cfg {String} altFormats
37115      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37116      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37117      */
37118     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37119     /**
37120      * @cfg {Array} disabledDays
37121      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37122      */
37123     disabledDays : null,
37124     /**
37125      * @cfg {String} disabledDaysText
37126      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37127      */
37128     disabledDaysText : "Disabled",
37129     /**
37130      * @cfg {Array} disabledDates
37131      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37132      * expression so they are very powerful. Some examples:
37133      * <ul>
37134      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37135      * <li>["03/08", "09/16"] would disable those days for every year</li>
37136      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37137      * <li>["03/../2006"] would disable every day in March 2006</li>
37138      * <li>["^03"] would disable every day in every March</li>
37139      * </ul>
37140      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37141      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37142      */
37143     disabledDates : null,
37144     /**
37145      * @cfg {String} disabledDatesText
37146      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37147      */
37148     disabledDatesText : "Disabled",
37149     /**
37150      * @cfg {Date/String} minValue
37151      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37152      * valid format (defaults to null).
37153      */
37154     minValue : null,
37155     /**
37156      * @cfg {Date/String} maxValue
37157      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37158      * valid format (defaults to null).
37159      */
37160     maxValue : null,
37161     /**
37162      * @cfg {String} minText
37163      * The error text to display when the date in the cell is before minValue (defaults to
37164      * 'The date in this field must be after {minValue}').
37165      */
37166     minText : "The date in this field must be equal to or after {0}",
37167     /**
37168      * @cfg {String} maxText
37169      * The error text to display when the date in the cell is after maxValue (defaults to
37170      * 'The date in this field must be before {maxValue}').
37171      */
37172     maxText : "The date in this field must be equal to or before {0}",
37173     /**
37174      * @cfg {String} invalidText
37175      * The error text to display when the date in the field is invalid (defaults to
37176      * '{value} is not a valid date - it must be in the format {format}').
37177      */
37178     invalidText : "{0} is not a valid date - it must be in the format {1}",
37179     /**
37180      * @cfg {String} triggerClass
37181      * An additional CSS class used to style the trigger button.  The trigger will always get the
37182      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37183      * which displays a calendar icon).
37184      */
37185     triggerClass : 'x-form-date-trigger',
37186     
37187
37188     /**
37189      * @cfg {bool} useIso
37190      * if enabled, then the date field will use a hidden field to store the 
37191      * real value as iso formated date. default (false)
37192      */ 
37193     useIso : false,
37194     /**
37195      * @cfg {String/Object} autoCreate
37196      * A DomHelper element spec, or true for a default element spec (defaults to
37197      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37198      */ 
37199     // private
37200     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37201     
37202     // private
37203     hiddenField: false,
37204     
37205     onRender : function(ct, position)
37206     {
37207         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37208         if (this.useIso) {
37209             this.el.dom.removeAttribute('name'); 
37210             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37211                     'before', true);
37212             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37213             // prevent input submission
37214             this.hiddenName = this.name;
37215         }
37216             
37217             
37218     },
37219     
37220     // private
37221     validateValue : function(value)
37222     {
37223         value = this.formatDate(value);
37224         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37225             return false;
37226         }
37227         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37228              return true;
37229         }
37230         var svalue = value;
37231         value = this.parseDate(value);
37232         if(!value){
37233             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37234             return false;
37235         }
37236         var time = value.getTime();
37237         if(this.minValue && time < this.minValue.getTime()){
37238             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37239             return false;
37240         }
37241         if(this.maxValue && time > this.maxValue.getTime()){
37242             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37243             return false;
37244         }
37245         if(this.disabledDays){
37246             var day = value.getDay();
37247             for(var i = 0; i < this.disabledDays.length; i++) {
37248                 if(day === this.disabledDays[i]){
37249                     this.markInvalid(this.disabledDaysText);
37250                     return false;
37251                 }
37252             }
37253         }
37254         var fvalue = this.formatDate(value);
37255         if(this.ddMatch && this.ddMatch.test(fvalue)){
37256             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37257             return false;
37258         }
37259         return true;
37260     },
37261
37262     // private
37263     // Provides logic to override the default TriggerField.validateBlur which just returns true
37264     validateBlur : function(){
37265         return !this.menu || !this.menu.isVisible();
37266     },
37267
37268     /**
37269      * Returns the current date value of the date field.
37270      * @return {Date} The date value
37271      */
37272     getValue : function(){
37273         
37274         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37275     },
37276
37277     /**
37278      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37279      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37280      * (the default format used is "m/d/y").
37281      * <br />Usage:
37282      * <pre><code>
37283 //All of these calls set the same date value (May 4, 2006)
37284
37285 //Pass a date object:
37286 var dt = new Date('5/4/06');
37287 dateField.setValue(dt);
37288
37289 //Pass a date string (default format):
37290 dateField.setValue('5/4/06');
37291
37292 //Pass a date string (custom format):
37293 dateField.format = 'Y-m-d';
37294 dateField.setValue('2006-5-4');
37295 </code></pre>
37296      * @param {String/Date} date The date or valid date string
37297      */
37298     setValue : function(date){
37299         if (this.hiddenField) {
37300             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37301         }
37302         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37303     },
37304
37305     // private
37306     parseDate : function(value){
37307         if(!value || value instanceof Date){
37308             return value;
37309         }
37310         var v = Date.parseDate(value, this.format);
37311         if(!v && this.altFormats){
37312             if(!this.altFormatsArray){
37313                 this.altFormatsArray = this.altFormats.split("|");
37314             }
37315             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37316                 v = Date.parseDate(value, this.altFormatsArray[i]);
37317             }
37318         }
37319         return v;
37320     },
37321
37322     // private
37323     formatDate : function(date, fmt){
37324         return (!date || !(date instanceof Date)) ?
37325                date : date.dateFormat(fmt || this.format);
37326     },
37327
37328     // private
37329     menuListeners : {
37330         select: function(m, d){
37331             this.setValue(d);
37332             this.fireEvent('select', this, d);
37333         },
37334         show : function(){ // retain focus styling
37335             this.onFocus();
37336         },
37337         hide : function(){
37338             this.focus.defer(10, this);
37339             var ml = this.menuListeners;
37340             this.menu.un("select", ml.select,  this);
37341             this.menu.un("show", ml.show,  this);
37342             this.menu.un("hide", ml.hide,  this);
37343         }
37344     },
37345
37346     // private
37347     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37348     onTriggerClick : function(){
37349         if(this.disabled){
37350             return;
37351         }
37352         if(this.menu == null){
37353             this.menu = new Roo.menu.DateMenu();
37354         }
37355         Roo.apply(this.menu.picker,  {
37356             showClear: this.allowBlank,
37357             minDate : this.minValue,
37358             maxDate : this.maxValue,
37359             disabledDatesRE : this.ddMatch,
37360             disabledDatesText : this.disabledDatesText,
37361             disabledDays : this.disabledDays,
37362             disabledDaysText : this.disabledDaysText,
37363             format : this.format,
37364             minText : String.format(this.minText, this.formatDate(this.minValue)),
37365             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37366         });
37367         this.menu.on(Roo.apply({}, this.menuListeners, {
37368             scope:this
37369         }));
37370         this.menu.picker.setValue(this.getValue() || new Date());
37371         this.menu.show(this.el, "tl-bl?");
37372     },
37373
37374     beforeBlur : function(){
37375         var v = this.parseDate(this.getRawValue());
37376         if(v){
37377             this.setValue(v);
37378         }
37379     }
37380
37381     /** @cfg {Boolean} grow @hide */
37382     /** @cfg {Number} growMin @hide */
37383     /** @cfg {Number} growMax @hide */
37384     /**
37385      * @hide
37386      * @method autoSize
37387      */
37388 });/*
37389  * Based on:
37390  * Ext JS Library 1.1.1
37391  * Copyright(c) 2006-2007, Ext JS, LLC.
37392  *
37393  * Originally Released Under LGPL - original licence link has changed is not relivant.
37394  *
37395  * Fork - LGPL
37396  * <script type="text/javascript">
37397  */
37398  
37399
37400 /**
37401  * @class Roo.form.ComboBox
37402  * @extends Roo.form.TriggerField
37403  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37404  * @constructor
37405  * Create a new ComboBox.
37406  * @param {Object} config Configuration options
37407  */
37408 Roo.form.ComboBox = function(config){
37409     Roo.form.ComboBox.superclass.constructor.call(this, config);
37410     this.addEvents({
37411         /**
37412          * @event expand
37413          * Fires when the dropdown list is expanded
37414              * @param {Roo.form.ComboBox} combo This combo box
37415              */
37416         'expand' : true,
37417         /**
37418          * @event collapse
37419          * Fires when the dropdown list is collapsed
37420              * @param {Roo.form.ComboBox} combo This combo box
37421              */
37422         'collapse' : true,
37423         /**
37424          * @event beforeselect
37425          * Fires before a list item is selected. Return false to cancel the selection.
37426              * @param {Roo.form.ComboBox} combo This combo box
37427              * @param {Roo.data.Record} record The data record returned from the underlying store
37428              * @param {Number} index The index of the selected item in the dropdown list
37429              */
37430         'beforeselect' : true,
37431         /**
37432          * @event select
37433          * Fires when a list item is selected
37434              * @param {Roo.form.ComboBox} combo This combo box
37435              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37436              * @param {Number} index The index of the selected item in the dropdown list
37437              */
37438         'select' : true,
37439         /**
37440          * @event beforequery
37441          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37442          * The event object passed has these properties:
37443              * @param {Roo.form.ComboBox} combo This combo box
37444              * @param {String} query The query
37445              * @param {Boolean} forceAll true to force "all" query
37446              * @param {Boolean} cancel true to cancel the query
37447              * @param {Object} e The query event object
37448              */
37449         'beforequery': true,
37450          /**
37451          * @event add
37452          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37453              * @param {Roo.form.ComboBox} combo This combo box
37454              */
37455         'add' : true,
37456         /**
37457          * @event edit
37458          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37459              * @param {Roo.form.ComboBox} combo This combo box
37460              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37461              */
37462         'edit' : true
37463         
37464         
37465     });
37466     if(this.transform){
37467         this.allowDomMove = false;
37468         var s = Roo.getDom(this.transform);
37469         if(!this.hiddenName){
37470             this.hiddenName = s.name;
37471         }
37472         if(!this.store){
37473             this.mode = 'local';
37474             var d = [], opts = s.options;
37475             for(var i = 0, len = opts.length;i < len; i++){
37476                 var o = opts[i];
37477                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37478                 if(o.selected) {
37479                     this.value = value;
37480                 }
37481                 d.push([value, o.text]);
37482             }
37483             this.store = new Roo.data.SimpleStore({
37484                 'id': 0,
37485                 fields: ['value', 'text'],
37486                 data : d
37487             });
37488             this.valueField = 'value';
37489             this.displayField = 'text';
37490         }
37491         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37492         if(!this.lazyRender){
37493             this.target = true;
37494             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37495             s.parentNode.removeChild(s); // remove it
37496             this.render(this.el.parentNode);
37497         }else{
37498             s.parentNode.removeChild(s); // remove it
37499         }
37500
37501     }
37502     if (this.store) {
37503         this.store = Roo.factory(this.store, Roo.data);
37504     }
37505     
37506     this.selectedIndex = -1;
37507     if(this.mode == 'local'){
37508         if(config.queryDelay === undefined){
37509             this.queryDelay = 10;
37510         }
37511         if(config.minChars === undefined){
37512             this.minChars = 0;
37513         }
37514     }
37515 };
37516
37517 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37518     /**
37519      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37520      */
37521     /**
37522      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37523      * rendering into an Roo.Editor, defaults to false)
37524      */
37525     /**
37526      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37527      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37528      */
37529     /**
37530      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37531      */
37532     /**
37533      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37534      * the dropdown list (defaults to undefined, with no header element)
37535      */
37536
37537      /**
37538      * @cfg {String/Roo.Template} tpl The template to use to render the output
37539      */
37540      
37541     // private
37542     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37543     /**
37544      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37545      */
37546     listWidth: undefined,
37547     /**
37548      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37549      * mode = 'remote' or 'text' if mode = 'local')
37550      */
37551     displayField: undefined,
37552     /**
37553      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37554      * mode = 'remote' or 'value' if mode = 'local'). 
37555      * Note: use of a valueField requires the user make a selection
37556      * in order for a value to be mapped.
37557      */
37558     valueField: undefined,
37559     
37560     
37561     /**
37562      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37563      * field's data value (defaults to the underlying DOM element's name)
37564      */
37565     hiddenName: undefined,
37566     /**
37567      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37568      */
37569     listClass: '',
37570     /**
37571      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37572      */
37573     selectedClass: 'x-combo-selected',
37574     /**
37575      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37576      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37577      * which displays a downward arrow icon).
37578      */
37579     triggerClass : 'x-form-arrow-trigger',
37580     /**
37581      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37582      */
37583     shadow:'sides',
37584     /**
37585      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37586      * anchor positions (defaults to 'tl-bl')
37587      */
37588     listAlign: 'tl-bl?',
37589     /**
37590      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37591      */
37592     maxHeight: 300,
37593     /**
37594      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37595      * query specified by the allQuery config option (defaults to 'query')
37596      */
37597     triggerAction: 'query',
37598     /**
37599      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37600      * (defaults to 4, does not apply if editable = false)
37601      */
37602     minChars : 4,
37603     /**
37604      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37605      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37606      */
37607     typeAhead: false,
37608     /**
37609      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37610      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37611      */
37612     queryDelay: 500,
37613     /**
37614      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37615      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37616      */
37617     pageSize: 0,
37618     /**
37619      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37620      * when editable = true (defaults to false)
37621      */
37622     selectOnFocus:false,
37623     /**
37624      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37625      */
37626     queryParam: 'query',
37627     /**
37628      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37629      * when mode = 'remote' (defaults to 'Loading...')
37630      */
37631     loadingText: 'Loading...',
37632     /**
37633      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37634      */
37635     resizable: false,
37636     /**
37637      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37638      */
37639     handleHeight : 8,
37640     /**
37641      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37642      * traditional select (defaults to true)
37643      */
37644     editable: true,
37645     /**
37646      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37647      */
37648     allQuery: '',
37649     /**
37650      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37651      */
37652     mode: 'remote',
37653     /**
37654      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37655      * listWidth has a higher value)
37656      */
37657     minListWidth : 70,
37658     /**
37659      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37660      * allow the user to set arbitrary text into the field (defaults to false)
37661      */
37662     forceSelection:false,
37663     /**
37664      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37665      * if typeAhead = true (defaults to 250)
37666      */
37667     typeAheadDelay : 250,
37668     /**
37669      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37670      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37671      */
37672     valueNotFoundText : undefined,
37673     /**
37674      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37675      */
37676     blockFocus : false,
37677     
37678     /**
37679      * @cfg {Boolean} disableClear Disable showing of clear button.
37680      */
37681     disableClear : false,
37682     /**
37683      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37684      */
37685     alwaysQuery : false,
37686     
37687     //private
37688     addicon : false,
37689     editicon: false,
37690     
37691     // element that contains real text value.. (when hidden is used..)
37692      
37693     // private
37694     onRender : function(ct, position){
37695         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37696         if(this.hiddenName){
37697             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37698                     'before', true);
37699             this.hiddenField.value =
37700                 this.hiddenValue !== undefined ? this.hiddenValue :
37701                 this.value !== undefined ? this.value : '';
37702
37703             // prevent input submission
37704             this.el.dom.removeAttribute('name');
37705              
37706              
37707         }
37708         if(Roo.isGecko){
37709             this.el.dom.setAttribute('autocomplete', 'off');
37710         }
37711
37712         var cls = 'x-combo-list';
37713
37714         this.list = new Roo.Layer({
37715             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37716         });
37717
37718         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37719         this.list.setWidth(lw);
37720         this.list.swallowEvent('mousewheel');
37721         this.assetHeight = 0;
37722
37723         if(this.title){
37724             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37725             this.assetHeight += this.header.getHeight();
37726         }
37727
37728         this.innerList = this.list.createChild({cls:cls+'-inner'});
37729         this.innerList.on('mouseover', this.onViewOver, this);
37730         this.innerList.on('mousemove', this.onViewMove, this);
37731         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37732         
37733         if(this.allowBlank && !this.pageSize && !this.disableClear){
37734             this.footer = this.list.createChild({cls:cls+'-ft'});
37735             this.pageTb = new Roo.Toolbar(this.footer);
37736            
37737         }
37738         if(this.pageSize){
37739             this.footer = this.list.createChild({cls:cls+'-ft'});
37740             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37741                     {pageSize: this.pageSize});
37742             
37743         }
37744         
37745         if (this.pageTb && this.allowBlank && !this.disableClear) {
37746             var _this = this;
37747             this.pageTb.add(new Roo.Toolbar.Fill(), {
37748                 cls: 'x-btn-icon x-btn-clear',
37749                 text: '&#160;',
37750                 handler: function()
37751                 {
37752                     _this.collapse();
37753                     _this.clearValue();
37754                     _this.onSelect(false, -1);
37755                 }
37756             });
37757         }
37758         if (this.footer) {
37759             this.assetHeight += this.footer.getHeight();
37760         }
37761         
37762
37763         if(!this.tpl){
37764             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37765         }
37766
37767         this.view = new Roo.View(this.innerList, this.tpl, {
37768             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37769         });
37770
37771         this.view.on('click', this.onViewClick, this);
37772
37773         this.store.on('beforeload', this.onBeforeLoad, this);
37774         this.store.on('load', this.onLoad, this);
37775         this.store.on('loadexception', this.onLoadException, this);
37776
37777         if(this.resizable){
37778             this.resizer = new Roo.Resizable(this.list,  {
37779                pinned:true, handles:'se'
37780             });
37781             this.resizer.on('resize', function(r, w, h){
37782                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37783                 this.listWidth = w;
37784                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37785                 this.restrictHeight();
37786             }, this);
37787             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37788         }
37789         if(!this.editable){
37790             this.editable = true;
37791             this.setEditable(false);
37792         }  
37793         
37794         
37795         if (typeof(this.events.add.listeners) != 'undefined') {
37796             
37797             this.addicon = this.wrap.createChild(
37798                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37799        
37800             this.addicon.on('click', function(e) {
37801                 this.fireEvent('add', this);
37802             }, this);
37803         }
37804         if (typeof(this.events.edit.listeners) != 'undefined') {
37805             
37806             this.editicon = this.wrap.createChild(
37807                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37808             if (this.addicon) {
37809                 this.editicon.setStyle('margin-left', '40px');
37810             }
37811             this.editicon.on('click', function(e) {
37812                 
37813                 // we fire even  if inothing is selected..
37814                 this.fireEvent('edit', this, this.lastData );
37815                 
37816             }, this);
37817         }
37818         
37819         
37820         
37821     },
37822
37823     // private
37824     initEvents : function(){
37825         Roo.form.ComboBox.superclass.initEvents.call(this);
37826
37827         this.keyNav = new Roo.KeyNav(this.el, {
37828             "up" : function(e){
37829                 this.inKeyMode = true;
37830                 this.selectPrev();
37831             },
37832
37833             "down" : function(e){
37834                 if(!this.isExpanded()){
37835                     this.onTriggerClick();
37836                 }else{
37837                     this.inKeyMode = true;
37838                     this.selectNext();
37839                 }
37840             },
37841
37842             "enter" : function(e){
37843                 this.onViewClick();
37844                 //return true;
37845             },
37846
37847             "esc" : function(e){
37848                 this.collapse();
37849             },
37850
37851             "tab" : function(e){
37852                 this.onViewClick(false);
37853                 this.fireEvent("specialkey", this, e);
37854                 return true;
37855             },
37856
37857             scope : this,
37858
37859             doRelay : function(foo, bar, hname){
37860                 if(hname == 'down' || this.scope.isExpanded()){
37861                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37862                 }
37863                 return true;
37864             },
37865
37866             forceKeyDown: true
37867         });
37868         this.queryDelay = Math.max(this.queryDelay || 10,
37869                 this.mode == 'local' ? 10 : 250);
37870         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37871         if(this.typeAhead){
37872             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37873         }
37874         if(this.editable !== false){
37875             this.el.on("keyup", this.onKeyUp, this);
37876         }
37877         if(this.forceSelection){
37878             this.on('blur', this.doForce, this);
37879         }
37880     },
37881
37882     onDestroy : function(){
37883         if(this.view){
37884             this.view.setStore(null);
37885             this.view.el.removeAllListeners();
37886             this.view.el.remove();
37887             this.view.purgeListeners();
37888         }
37889         if(this.list){
37890             this.list.destroy();
37891         }
37892         if(this.store){
37893             this.store.un('beforeload', this.onBeforeLoad, this);
37894             this.store.un('load', this.onLoad, this);
37895             this.store.un('loadexception', this.onLoadException, this);
37896         }
37897         Roo.form.ComboBox.superclass.onDestroy.call(this);
37898     },
37899
37900     // private
37901     fireKey : function(e){
37902         if(e.isNavKeyPress() && !this.list.isVisible()){
37903             this.fireEvent("specialkey", this, e);
37904         }
37905     },
37906
37907     // private
37908     onResize: function(w, h){
37909         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37910         
37911         if(typeof w != 'number'){
37912             // we do not handle it!?!?
37913             return;
37914         }
37915         var tw = this.trigger.getWidth();
37916         tw += this.addicon ? this.addicon.getWidth() : 0;
37917         tw += this.editicon ? this.editicon.getWidth() : 0;
37918         var x = w - tw;
37919         this.el.setWidth( this.adjustWidth('input', x));
37920             
37921         this.trigger.setStyle('left', x+'px');
37922         
37923         if(this.list && this.listWidth === undefined){
37924             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37925             this.list.setWidth(lw);
37926             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37927         }
37928         
37929     
37930         
37931     },
37932
37933     /**
37934      * Allow or prevent the user from directly editing the field text.  If false is passed,
37935      * the user will only be able to select from the items defined in the dropdown list.  This method
37936      * is the runtime equivalent of setting the 'editable' config option at config time.
37937      * @param {Boolean} value True to allow the user to directly edit the field text
37938      */
37939     setEditable : function(value){
37940         if(value == this.editable){
37941             return;
37942         }
37943         this.editable = value;
37944         if(!value){
37945             this.el.dom.setAttribute('readOnly', true);
37946             this.el.on('mousedown', this.onTriggerClick,  this);
37947             this.el.addClass('x-combo-noedit');
37948         }else{
37949             this.el.dom.setAttribute('readOnly', false);
37950             this.el.un('mousedown', this.onTriggerClick,  this);
37951             this.el.removeClass('x-combo-noedit');
37952         }
37953     },
37954
37955     // private
37956     onBeforeLoad : function(){
37957         if(!this.hasFocus){
37958             return;
37959         }
37960         this.innerList.update(this.loadingText ?
37961                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37962         this.restrictHeight();
37963         this.selectedIndex = -1;
37964     },
37965
37966     // private
37967     onLoad : function(){
37968         if(!this.hasFocus){
37969             return;
37970         }
37971         if(this.store.getCount() > 0){
37972             this.expand();
37973             this.restrictHeight();
37974             if(this.lastQuery == this.allQuery){
37975                 if(this.editable){
37976                     this.el.dom.select();
37977                 }
37978                 if(!this.selectByValue(this.value, true)){
37979                     this.select(0, true);
37980                 }
37981             }else{
37982                 this.selectNext();
37983                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37984                     this.taTask.delay(this.typeAheadDelay);
37985                 }
37986             }
37987         }else{
37988             this.onEmptyResults();
37989         }
37990         //this.el.focus();
37991     },
37992     // private
37993     onLoadException : function()
37994     {
37995         this.collapse();
37996         Roo.log(this.store.reader.jsonData);
37997         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37998             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37999         }
38000         
38001         
38002     },
38003     // private
38004     onTypeAhead : function(){
38005         if(this.store.getCount() > 0){
38006             var r = this.store.getAt(0);
38007             var newValue = r.data[this.displayField];
38008             var len = newValue.length;
38009             var selStart = this.getRawValue().length;
38010             if(selStart != len){
38011                 this.setRawValue(newValue);
38012                 this.selectText(selStart, newValue.length);
38013             }
38014         }
38015     },
38016
38017     // private
38018     onSelect : function(record, index){
38019         if(this.fireEvent('beforeselect', this, record, index) !== false){
38020             this.setFromData(index > -1 ? record.data : false);
38021             this.collapse();
38022             this.fireEvent('select', this, record, index);
38023         }
38024     },
38025
38026     /**
38027      * Returns the currently selected field value or empty string if no value is set.
38028      * @return {String} value The selected value
38029      */
38030     getValue : function(){
38031         if(this.valueField){
38032             return typeof this.value != 'undefined' ? this.value : '';
38033         }else{
38034             return Roo.form.ComboBox.superclass.getValue.call(this);
38035         }
38036     },
38037
38038     /**
38039      * Clears any text/value currently set in the field
38040      */
38041     clearValue : function(){
38042         if(this.hiddenField){
38043             this.hiddenField.value = '';
38044         }
38045         this.value = '';
38046         this.setRawValue('');
38047         this.lastSelectionText = '';
38048         this.applyEmptyText();
38049     },
38050
38051     /**
38052      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38053      * will be displayed in the field.  If the value does not match the data value of an existing item,
38054      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38055      * Otherwise the field will be blank (although the value will still be set).
38056      * @param {String} value The value to match
38057      */
38058     setValue : function(v){
38059         var text = v;
38060         if(this.valueField){
38061             var r = this.findRecord(this.valueField, v);
38062             if(r){
38063                 text = r.data[this.displayField];
38064             }else if(this.valueNotFoundText !== undefined){
38065                 text = this.valueNotFoundText;
38066             }
38067         }
38068         this.lastSelectionText = text;
38069         if(this.hiddenField){
38070             this.hiddenField.value = v;
38071         }
38072         Roo.form.ComboBox.superclass.setValue.call(this, text);
38073         this.value = v;
38074     },
38075     /**
38076      * @property {Object} the last set data for the element
38077      */
38078     
38079     lastData : false,
38080     /**
38081      * Sets the value of the field based on a object which is related to the record format for the store.
38082      * @param {Object} value the value to set as. or false on reset?
38083      */
38084     setFromData : function(o){
38085         var dv = ''; // display value
38086         var vv = ''; // value value..
38087         this.lastData = o;
38088         if (this.displayField) {
38089             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38090         } else {
38091             // this is an error condition!!!
38092             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38093         }
38094         
38095         if(this.valueField){
38096             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38097         }
38098         if(this.hiddenField){
38099             this.hiddenField.value = vv;
38100             
38101             this.lastSelectionText = dv;
38102             Roo.form.ComboBox.superclass.setValue.call(this, dv);
38103             this.value = vv;
38104             return;
38105         }
38106         // no hidden field.. - we store the value in 'value', but still display
38107         // display field!!!!
38108         this.lastSelectionText = dv;
38109         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38110         this.value = vv;
38111         
38112         
38113     },
38114     // private
38115     reset : function(){
38116         // overridden so that last data is reset..
38117         this.setValue(this.originalValue);
38118         this.clearInvalid();
38119         this.lastData = false;
38120     },
38121     // private
38122     findRecord : function(prop, value){
38123         var record;
38124         if(this.store.getCount() > 0){
38125             this.store.each(function(r){
38126                 if(r.data[prop] == value){
38127                     record = r;
38128                     return false;
38129                 }
38130                 return true;
38131             });
38132         }
38133         return record;
38134     },
38135     
38136     getName: function()
38137     {
38138         // returns hidden if it's set..
38139         if (!this.rendered) {return ''};
38140         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38141         
38142     },
38143     // private
38144     onViewMove : function(e, t){
38145         this.inKeyMode = false;
38146     },
38147
38148     // private
38149     onViewOver : function(e, t){
38150         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38151             return;
38152         }
38153         var item = this.view.findItemFromChild(t);
38154         if(item){
38155             var index = this.view.indexOf(item);
38156             this.select(index, false);
38157         }
38158     },
38159
38160     // private
38161     onViewClick : function(doFocus)
38162     {
38163         var index = this.view.getSelectedIndexes()[0];
38164         var r = this.store.getAt(index);
38165         if(r){
38166             this.onSelect(r, index);
38167         }
38168         if(doFocus !== false && !this.blockFocus){
38169             this.el.focus();
38170         }
38171     },
38172
38173     // private
38174     restrictHeight : function(){
38175         this.innerList.dom.style.height = '';
38176         var inner = this.innerList.dom;
38177         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38178         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38179         this.list.beginUpdate();
38180         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38181         this.list.alignTo(this.el, this.listAlign);
38182         this.list.endUpdate();
38183     },
38184
38185     // private
38186     onEmptyResults : function(){
38187         this.collapse();
38188     },
38189
38190     /**
38191      * Returns true if the dropdown list is expanded, else false.
38192      */
38193     isExpanded : function(){
38194         return this.list.isVisible();
38195     },
38196
38197     /**
38198      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38199      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38200      * @param {String} value The data value of the item to select
38201      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38202      * selected item if it is not currently in view (defaults to true)
38203      * @return {Boolean} True if the value matched an item in the list, else false
38204      */
38205     selectByValue : function(v, scrollIntoView){
38206         if(v !== undefined && v !== null){
38207             var r = this.findRecord(this.valueField || this.displayField, v);
38208             if(r){
38209                 this.select(this.store.indexOf(r), scrollIntoView);
38210                 return true;
38211             }
38212         }
38213         return false;
38214     },
38215
38216     /**
38217      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38218      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38219      * @param {Number} index The zero-based index of the list item to select
38220      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38221      * selected item if it is not currently in view (defaults to true)
38222      */
38223     select : function(index, scrollIntoView){
38224         this.selectedIndex = index;
38225         this.view.select(index);
38226         if(scrollIntoView !== false){
38227             var el = this.view.getNode(index);
38228             if(el){
38229                 this.innerList.scrollChildIntoView(el, false);
38230             }
38231         }
38232     },
38233
38234     // private
38235     selectNext : function(){
38236         var ct = this.store.getCount();
38237         if(ct > 0){
38238             if(this.selectedIndex == -1){
38239                 this.select(0);
38240             }else if(this.selectedIndex < ct-1){
38241                 this.select(this.selectedIndex+1);
38242             }
38243         }
38244     },
38245
38246     // private
38247     selectPrev : function(){
38248         var ct = this.store.getCount();
38249         if(ct > 0){
38250             if(this.selectedIndex == -1){
38251                 this.select(0);
38252             }else if(this.selectedIndex != 0){
38253                 this.select(this.selectedIndex-1);
38254             }
38255         }
38256     },
38257
38258     // private
38259     onKeyUp : function(e){
38260         if(this.editable !== false && !e.isSpecialKey()){
38261             this.lastKey = e.getKey();
38262             this.dqTask.delay(this.queryDelay);
38263         }
38264     },
38265
38266     // private
38267     validateBlur : function(){
38268         return !this.list || !this.list.isVisible();   
38269     },
38270
38271     // private
38272     initQuery : function(){
38273         this.doQuery(this.getRawValue());
38274     },
38275
38276     // private
38277     doForce : function(){
38278         if(this.el.dom.value.length > 0){
38279             this.el.dom.value =
38280                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38281             this.applyEmptyText();
38282         }
38283     },
38284
38285     /**
38286      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38287      * query allowing the query action to be canceled if needed.
38288      * @param {String} query The SQL query to execute
38289      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38290      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38291      * saved in the current store (defaults to false)
38292      */
38293     doQuery : function(q, forceAll){
38294         if(q === undefined || q === null){
38295             q = '';
38296         }
38297         var qe = {
38298             query: q,
38299             forceAll: forceAll,
38300             combo: this,
38301             cancel:false
38302         };
38303         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38304             return false;
38305         }
38306         q = qe.query;
38307         forceAll = qe.forceAll;
38308         if(forceAll === true || (q.length >= this.minChars)){
38309             if(this.lastQuery != q || this.alwaysQuery){
38310                 this.lastQuery = q;
38311                 if(this.mode == 'local'){
38312                     this.selectedIndex = -1;
38313                     if(forceAll){
38314                         this.store.clearFilter();
38315                     }else{
38316                         this.store.filter(this.displayField, q);
38317                     }
38318                     this.onLoad();
38319                 }else{
38320                     this.store.baseParams[this.queryParam] = q;
38321                     this.store.load({
38322                         params: this.getParams(q)
38323                     });
38324                     this.expand();
38325                 }
38326             }else{
38327                 this.selectedIndex = -1;
38328                 this.onLoad();   
38329             }
38330         }
38331     },
38332
38333     // private
38334     getParams : function(q){
38335         var p = {};
38336         //p[this.queryParam] = q;
38337         if(this.pageSize){
38338             p.start = 0;
38339             p.limit = this.pageSize;
38340         }
38341         return p;
38342     },
38343
38344     /**
38345      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38346      */
38347     collapse : function(){
38348         if(!this.isExpanded()){
38349             return;
38350         }
38351         this.list.hide();
38352         Roo.get(document).un('mousedown', this.collapseIf, this);
38353         Roo.get(document).un('mousewheel', this.collapseIf, this);
38354         if (!this.editable) {
38355             Roo.get(document).un('keydown', this.listKeyPress, this);
38356         }
38357         this.fireEvent('collapse', this);
38358     },
38359
38360     // private
38361     collapseIf : function(e){
38362         if(!e.within(this.wrap) && !e.within(this.list)){
38363             this.collapse();
38364         }
38365     },
38366
38367     /**
38368      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38369      */
38370     expand : function(){
38371         if(this.isExpanded() || !this.hasFocus){
38372             return;
38373         }
38374         this.list.alignTo(this.el, this.listAlign);
38375         this.list.show();
38376         Roo.get(document).on('mousedown', this.collapseIf, this);
38377         Roo.get(document).on('mousewheel', this.collapseIf, this);
38378         if (!this.editable) {
38379             Roo.get(document).on('keydown', this.listKeyPress, this);
38380         }
38381         
38382         this.fireEvent('expand', this);
38383     },
38384
38385     // private
38386     // Implements the default empty TriggerField.onTriggerClick function
38387     onTriggerClick : function(){
38388         if(this.disabled){
38389             return;
38390         }
38391         if(this.isExpanded()){
38392             this.collapse();
38393             if (!this.blockFocus) {
38394                 this.el.focus();
38395             }
38396             
38397         }else {
38398             this.hasFocus = true;
38399             if(this.triggerAction == 'all') {
38400                 this.doQuery(this.allQuery, true);
38401             } else {
38402                 this.doQuery(this.getRawValue());
38403             }
38404             if (!this.blockFocus) {
38405                 this.el.focus();
38406             }
38407         }
38408     },
38409     listKeyPress : function(e)
38410     {
38411         //Roo.log('listkeypress');
38412         // scroll to first matching element based on key pres..
38413         if (e.isSpecialKey()) {
38414             return false;
38415         }
38416         var k = String.fromCharCode(e.getKey()).toUpperCase();
38417         //Roo.log(k);
38418         var match  = false;
38419         var csel = this.view.getSelectedNodes();
38420         var cselitem = false;
38421         if (csel.length) {
38422             var ix = this.view.indexOf(csel[0]);
38423             cselitem  = this.store.getAt(ix);
38424             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38425                 cselitem = false;
38426             }
38427             
38428         }
38429         
38430         this.store.each(function(v) { 
38431             if (cselitem) {
38432                 // start at existing selection.
38433                 if (cselitem.id == v.id) {
38434                     cselitem = false;
38435                 }
38436                 return;
38437             }
38438                 
38439             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38440                 match = this.store.indexOf(v);
38441                 return false;
38442             }
38443         }, this);
38444         
38445         if (match === false) {
38446             return true; // no more action?
38447         }
38448         // scroll to?
38449         this.view.select(match);
38450         var sn = Roo.get(this.view.getSelectedNodes()[0])
38451         sn.scrollIntoView(sn.dom.parentNode, false);
38452     }
38453
38454     /** 
38455     * @cfg {Boolean} grow 
38456     * @hide 
38457     */
38458     /** 
38459     * @cfg {Number} growMin 
38460     * @hide 
38461     */
38462     /** 
38463     * @cfg {Number} growMax 
38464     * @hide 
38465     */
38466     /**
38467      * @hide
38468      * @method autoSize
38469      */
38470 });/*
38471  * Copyright(c) 2010-2012, Roo J Solutions Limited
38472  *
38473  * Licence LGPL
38474  *
38475  */
38476
38477 /**
38478  * @class Roo.form.ComboBoxArray
38479  * @extends Roo.form.TextField
38480  * A facebook style adder... for lists of email / people / countries  etc...
38481  * pick multiple items from a combo box, and shows each one.
38482  *
38483  *  Fred [x]  Brian [x]  [Pick another |v]
38484  *
38485  *
38486  *  For this to work: it needs various extra information
38487  *    - normal combo problay has
38488  *      name, hiddenName
38489  *    + displayField, valueField
38490  *
38491  *    For our purpose...
38492  *
38493  *
38494  *   If we change from 'extends' to wrapping...
38495  *   
38496  *  
38497  *
38498  
38499  
38500  * @constructor
38501  * Create a new ComboBoxArray.
38502  * @param {Object} config Configuration options
38503  */
38504  
38505
38506 Roo.form.ComboBoxArray = function(config)
38507 {
38508     
38509     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
38510     
38511     this.items = new Roo.util.MixedCollection(false);
38512     
38513     // construct the child combo...
38514     
38515     
38516     
38517     
38518    
38519     
38520 }
38521
38522  
38523 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
38524
38525     /**
38526      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
38527      */
38528     
38529     lastData : false,
38530     
38531     // behavies liek a hiddne field
38532     inputType:      'hidden',
38533     /**
38534      * @cfg {Number} width The width of the box that displays the selected element
38535      */ 
38536     width:          300,
38537
38538     
38539     
38540     /**
38541      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
38542      */
38543     name : false,
38544     /**
38545      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
38546      */
38547     hiddenName : false,
38548     
38549     
38550     // private the array of items that are displayed..
38551     items  : false,
38552     // private - the hidden field el.
38553     hiddenEl : false,
38554     // private - the filed el..
38555     el : false,
38556     
38557     //validateValue : function() { return true; }, // all values are ok!
38558     //onAddClick: function() { },
38559     
38560     onRender : function(ct, position) 
38561     {
38562         
38563         // create the standard hidden element
38564         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
38565         
38566         
38567         // give fake names to child combo;
38568         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
38569         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
38570         
38571         this.combo = Roo.factory(this.combo, Roo.form);
38572         this.combo.onRender(ct, position);
38573         
38574         // assigned so form know we need to do this..
38575         this.store          = this.combo.store;
38576         this.valueField     = this.combo.valueField;
38577         this.displayField   = this.combo.displayField ;
38578         
38579         
38580         this.combo.wrap.addClass('x-cbarray-grp');
38581         
38582         var cbwrap = this.combo.wrap.createChild(
38583             {tag: 'div', cls: 'x-cbarray-cb'},
38584             this.combo.el.dom
38585         );
38586         
38587              
38588         this.hiddenEl = this.combo.wrap.createChild({
38589             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
38590         });
38591         this.el = this.combo.wrap.createChild({
38592             tag: 'input',  type:'hidden' , name: this.name, value : ''
38593         });
38594          //   this.el.dom.removeAttribute("name");
38595         
38596         
38597         this.outerWrap = this.combo.wrap;
38598         this.wrap = cbwrap;
38599         
38600         this.outerWrap.setWidth(this.width);
38601         this.outerWrap.dom.removeChild(this.el.dom);
38602         
38603         this.wrap.dom.appendChild(this.el.dom);
38604         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
38605         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
38606         
38607         this.combo.trigger.setStyle('position','relative');
38608         this.combo.trigger.setStyle('left', '0px');
38609         this.combo.trigger.setStyle('top', '2px');
38610         
38611         this.combo.el.setStyle('vertical-align', 'text-bottom');
38612         
38613         //this.trigger.setStyle('vertical-align', 'top');
38614         
38615         // this should use the code from combo really... on('add' ....)
38616         if (this.adder) {
38617             
38618         
38619             this.adder = this.outerWrap.createChild(
38620                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
38621             var _t = this;
38622             this.adder.on('click', function(e) {
38623                 _t.fireEvent('adderclick', this, e);
38624             }, _t);
38625         }
38626         //var _t = this;
38627         //this.adder.on('click', this.onAddClick, _t);
38628         
38629         
38630         this.combo.on('select', function(cb, rec, ix) {
38631             this.addItem(rec.data);
38632             
38633             cb.setValue('');
38634             cb.el.dom.value = '';
38635             //cb.lastData = rec.data;
38636             // add to list
38637             
38638         }, this);
38639         
38640         
38641     },
38642     
38643     
38644     getName: function()
38645     {
38646         // returns hidden if it's set..
38647         if (!this.rendered) {return ''};
38648         return  this.hiddenName ? this.hiddenName : this.name;
38649         
38650     },
38651     
38652     
38653     onResize: function(w, h){
38654         
38655         return;
38656         // not sure if this is needed..
38657         //this.combo.onResize(w,h);
38658         
38659         if(typeof w != 'number'){
38660             // we do not handle it!?!?
38661             return;
38662         }
38663         var tw = this.combo.trigger.getWidth();
38664         tw += this.addicon ? this.addicon.getWidth() : 0;
38665         tw += this.editicon ? this.editicon.getWidth() : 0;
38666         var x = w - tw;
38667         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
38668             
38669         this.combo.trigger.setStyle('left', '0px');
38670         
38671         if(this.list && this.listWidth === undefined){
38672             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
38673             this.list.setWidth(lw);
38674             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38675         }
38676         
38677     
38678         
38679     },
38680     
38681     addItem: function(rec)
38682     {
38683         var valueField = this.combo.valueField;
38684         var displayField = this.combo.displayField;
38685         if (this.items.indexOfKey(rec[valueField]) > -1) {
38686             //console.log("GOT " + rec.data.id);
38687             return;
38688         }
38689         
38690         var x = new Roo.form.ComboBoxArray.Item({
38691             //id : rec[this.idField],
38692             data : rec,
38693             displayField : displayField ,
38694             tipField : displayField ,
38695             cb : this
38696         });
38697         // use the 
38698         this.items.add(rec[valueField],x);
38699         // add it before the element..
38700         this.updateHiddenEl();
38701         x.render(this.outerWrap, this.wrap.dom);
38702         // add the image handler..
38703     },
38704     
38705     updateHiddenEl : function()
38706     {
38707         this.validate();
38708         if (!this.hiddenEl) {
38709             return;
38710         }
38711         var ar = [];
38712         var idField = this.combo.valueField;
38713         
38714         this.items.each(function(f) {
38715             ar.push(f.data[idField]);
38716            
38717         });
38718         this.hiddenEl.dom.value = ar.join(',');
38719         this.validate();
38720     },
38721     
38722     reset : function()
38723     {
38724         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
38725         this.items.each(function(f) {
38726            f.remove(); 
38727         });
38728         this.el.dom.value = '';
38729         if (this.hiddenEl) {
38730             this.hiddenEl.dom.value = '';
38731         }
38732         
38733     },
38734     getValue: function()
38735     {
38736         return this.hiddenEl ? this.hiddenEl.dom.value : '';
38737     },
38738     setValue: function(v) // not a valid action - must use addItems..
38739     {
38740          
38741         this.reset();
38742         
38743         
38744         
38745         if (this.store.isLocal && (typeof(v) == 'string')) {
38746             // then we can use the store to find the values..
38747             // comma seperated at present.. this needs to allow JSON based encoding..
38748             this.hiddenEl.value  = v;
38749             var v_ar = [];
38750             Roo.each(v.split(','), function(k) {
38751                 Roo.log("CHECK " + this.valueField + ',' + k);
38752                 var li = this.store.query(this.valueField, k);
38753                 if (!li.length) {
38754                     return;
38755                 }
38756                 add = {};
38757                 add[this.valueField] = k;
38758                 add[this.displayField] = li.item(0).data[this.displayField];
38759                 
38760                 this.addItem(add);
38761             }, this) 
38762             
38763                 
38764             
38765         }
38766         if (typeof(v) == 'object') {
38767             // then let's assume it's an array of objects..
38768             Roo.each(v, function(l) {
38769                 this.addItem(l);
38770             }, this);
38771              
38772         }
38773         
38774         
38775     },
38776     setFromData: function(v)
38777     {
38778         // this recieves an object, if setValues is called.
38779         this.reset();
38780         this.el.dom.value = v[this.displayField];
38781         this.hiddenEl.dom.value = v[this.valueField];
38782         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
38783             return;
38784         }
38785         var keys = v[this.valueField].split(',');
38786         var display = v[this.displayField].split(',');
38787         for (var i = 0 ; i < keys.length; i++) {
38788             
38789             add = {};
38790             add[this.valueField] = keys[i];
38791             add[this.displayField] = display[i];
38792             this.addItem(add);
38793         }
38794       
38795         
38796     },
38797     
38798     
38799     validateValue : function(value){
38800         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
38801         
38802     }
38803     
38804 });
38805
38806
38807
38808 /**
38809  * @class Roo.form.ComboBoxArray.Item
38810  * @extends Roo.BoxComponent
38811  * A selected item in the list
38812  *  Fred [x]  Brian [x]  [Pick another |v]
38813  * 
38814  * @constructor
38815  * Create a new item.
38816  * @param {Object} config Configuration options
38817  */
38818  
38819 Roo.form.ComboBoxArray.Item = function(config) {
38820     config.id = Roo.id();
38821     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
38822 }
38823
38824 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
38825     data : {},
38826     cb: false,
38827     displayField : false,
38828     tipField : false,
38829     
38830     
38831     defaultAutoCreate : {
38832         tag: 'div',
38833         cls: 'x-cbarray-item',
38834         cn : [ 
38835             { tag: 'div' },
38836             {
38837                 tag: 'img',
38838                 width:16,
38839                 height : 16,
38840                 src : Roo.BLANK_IMAGE_URL ,
38841                 align: 'center'
38842             }
38843         ]
38844         
38845     },
38846     
38847  
38848     onRender : function(ct, position)
38849     {
38850         Roo.form.Field.superclass.onRender.call(this, ct, position);
38851         
38852         if(!this.el){
38853             var cfg = this.getAutoCreate();
38854             this.el = ct.createChild(cfg, position);
38855         }
38856         
38857         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
38858         
38859         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
38860             this.cb.renderer(this.data) :
38861             String.format('{0}',this.data[this.displayField]);
38862         
38863             
38864         this.el.child('div').dom.setAttribute('qtip',
38865                         String.format('{0}',this.data[this.tipField])
38866         );
38867         
38868         this.el.child('img').on('click', this.remove, this);
38869         
38870     },
38871    
38872     remove : function()
38873     {
38874         
38875         this.cb.items.remove(this);
38876         this.el.child('img').un('click', this.remove, this);
38877         this.el.remove();
38878         this.cb.updateHiddenEl();
38879     }
38880     
38881     
38882 });/*
38883  * Based on:
38884  * Ext JS Library 1.1.1
38885  * Copyright(c) 2006-2007, Ext JS, LLC.
38886  *
38887  * Originally Released Under LGPL - original licence link has changed is not relivant.
38888  *
38889  * Fork - LGPL
38890  * <script type="text/javascript">
38891  */
38892 /**
38893  * @class Roo.form.Checkbox
38894  * @extends Roo.form.Field
38895  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38896  * @constructor
38897  * Creates a new Checkbox
38898  * @param {Object} config Configuration options
38899  */
38900 Roo.form.Checkbox = function(config){
38901     Roo.form.Checkbox.superclass.constructor.call(this, config);
38902     this.addEvents({
38903         /**
38904          * @event check
38905          * Fires when the checkbox is checked or unchecked.
38906              * @param {Roo.form.Checkbox} this This checkbox
38907              * @param {Boolean} checked The new checked value
38908              */
38909         check : true
38910     });
38911 };
38912
38913 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38914     /**
38915      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38916      */
38917     focusClass : undefined,
38918     /**
38919      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38920      */
38921     fieldClass: "x-form-field",
38922     /**
38923      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38924      */
38925     checked: false,
38926     /**
38927      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38928      * {tag: "input", type: "checkbox", autocomplete: "off"})
38929      */
38930     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38931     /**
38932      * @cfg {String} boxLabel The text that appears beside the checkbox
38933      */
38934     boxLabel : "",
38935     /**
38936      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38937      */  
38938     inputValue : '1',
38939     /**
38940      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38941      */
38942      valueOff: '0', // value when not checked..
38943
38944     actionMode : 'viewEl', 
38945     //
38946     // private
38947     itemCls : 'x-menu-check-item x-form-item',
38948     groupClass : 'x-menu-group-item',
38949     inputType : 'hidden',
38950     
38951     
38952     inSetChecked: false, // check that we are not calling self...
38953     
38954     inputElement: false, // real input element?
38955     basedOn: false, // ????
38956     
38957     isFormField: true, // not sure where this is needed!!!!
38958
38959     onResize : function(){
38960         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38961         if(!this.boxLabel){
38962             this.el.alignTo(this.wrap, 'c-c');
38963         }
38964     },
38965
38966     initEvents : function(){
38967         Roo.form.Checkbox.superclass.initEvents.call(this);
38968         this.el.on("click", this.onClick,  this);
38969         this.el.on("change", this.onClick,  this);
38970     },
38971
38972
38973     getResizeEl : function(){
38974         return this.wrap;
38975     },
38976
38977     getPositionEl : function(){
38978         return this.wrap;
38979     },
38980
38981     // private
38982     onRender : function(ct, position){
38983         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38984         /*
38985         if(this.inputValue !== undefined){
38986             this.el.dom.value = this.inputValue;
38987         }
38988         */
38989         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38990         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38991         var viewEl = this.wrap.createChild({ 
38992             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38993         this.viewEl = viewEl;   
38994         this.wrap.on('click', this.onClick,  this); 
38995         
38996         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38997         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38998         
38999         
39000         
39001         if(this.boxLabel){
39002             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39003         //    viewEl.on('click', this.onClick,  this); 
39004         }
39005         //if(this.checked){
39006             this.setChecked(this.checked);
39007         //}else{
39008             //this.checked = this.el.dom;
39009         //}
39010
39011     },
39012
39013     // private
39014     initValue : Roo.emptyFn,
39015
39016     /**
39017      * Returns the checked state of the checkbox.
39018      * @return {Boolean} True if checked, else false
39019      */
39020     getValue : function(){
39021         if(this.el){
39022             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
39023         }
39024         return this.valueOff;
39025         
39026     },
39027
39028         // private
39029     onClick : function(){ 
39030         this.setChecked(!this.checked);
39031
39032         //if(this.el.dom.checked != this.checked){
39033         //    this.setValue(this.el.dom.checked);
39034        // }
39035     },
39036
39037     /**
39038      * Sets the checked state of the checkbox.
39039      * On is always based on a string comparison between inputValue and the param.
39040      * @param {Boolean/String} value - the value to set 
39041      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
39042      */
39043     setValue : function(v,suppressEvent){
39044         
39045         
39046         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
39047         //if(this.el && this.el.dom){
39048         //    this.el.dom.checked = this.checked;
39049         //    this.el.dom.defaultChecked = this.checked;
39050         //}
39051         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
39052         //this.fireEvent("check", this, this.checked);
39053     },
39054     // private..
39055     setChecked : function(state,suppressEvent)
39056     {
39057         if (this.inSetChecked) {
39058             this.checked = state;
39059             return;
39060         }
39061         
39062     
39063         if(this.wrap){
39064             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
39065         }
39066         this.checked = state;
39067         if(suppressEvent !== true){
39068             this.fireEvent('check', this, state);
39069         }
39070         this.inSetChecked = true;
39071         this.el.dom.value = state ? this.inputValue : this.valueOff;
39072         this.inSetChecked = false;
39073         
39074     },
39075     // handle setting of hidden value by some other method!!?!?
39076     setFromHidden: function()
39077     {
39078         if(!this.el){
39079             return;
39080         }
39081         //console.log("SET FROM HIDDEN");
39082         //alert('setFrom hidden');
39083         this.setValue(this.el.dom.value);
39084     },
39085     
39086     onDestroy : function()
39087     {
39088         if(this.viewEl){
39089             Roo.get(this.viewEl).remove();
39090         }
39091          
39092         Roo.form.Checkbox.superclass.onDestroy.call(this);
39093     }
39094
39095 });/*
39096  * Based on:
39097  * Ext JS Library 1.1.1
39098  * Copyright(c) 2006-2007, Ext JS, LLC.
39099  *
39100  * Originally Released Under LGPL - original licence link has changed is not relivant.
39101  *
39102  * Fork - LGPL
39103  * <script type="text/javascript">
39104  */
39105  
39106 /**
39107  * @class Roo.form.Radio
39108  * @extends Roo.form.Checkbox
39109  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
39110  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
39111  * @constructor
39112  * Creates a new Radio
39113  * @param {Object} config Configuration options
39114  */
39115 Roo.form.Radio = function(){
39116     Roo.form.Radio.superclass.constructor.apply(this, arguments);
39117 };
39118 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
39119     inputType: 'radio',
39120
39121     /**
39122      * If this radio is part of a group, it will return the selected value
39123      * @return {String}
39124      */
39125     getGroupValue : function(){
39126         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
39127     }
39128 });//<script type="text/javascript">
39129
39130 /*
39131  * Ext JS Library 1.1.1
39132  * Copyright(c) 2006-2007, Ext JS, LLC.
39133  * licensing@extjs.com
39134  * 
39135  * http://www.extjs.com/license
39136  */
39137  
39138  /*
39139   * 
39140   * Known bugs:
39141   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
39142   * - IE ? - no idea how much works there.
39143   * 
39144   * 
39145   * 
39146   */
39147  
39148
39149 /**
39150  * @class Ext.form.HtmlEditor
39151  * @extends Ext.form.Field
39152  * Provides a lightweight HTML Editor component.
39153  *
39154  * This has been tested on Fireforx / Chrome.. IE may not be so great..
39155  * 
39156  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
39157  * supported by this editor.</b><br/><br/>
39158  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
39159  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
39160  */
39161 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
39162       /**
39163      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
39164      */
39165     toolbars : false,
39166     /**
39167      * @cfg {String} createLinkText The default text for the create link prompt
39168      */
39169     createLinkText : 'Please enter the URL for the link:',
39170     /**
39171      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
39172      */
39173     defaultLinkValue : 'http:/'+'/',
39174    
39175      /**
39176      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
39177      *                        Roo.resizable.
39178      */
39179     resizable : false,
39180      /**
39181      * @cfg {Number} height (in pixels)
39182      */   
39183     height: 300,
39184    /**
39185      * @cfg {Number} width (in pixels)
39186      */   
39187     width: 500,
39188     
39189     /**
39190      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
39191      * 
39192      */
39193     stylesheets: false,
39194     
39195     // id of frame..
39196     frameId: false,
39197     
39198     // private properties
39199     validationEvent : false,
39200     deferHeight: true,
39201     initialized : false,
39202     activated : false,
39203     sourceEditMode : false,
39204     onFocus : Roo.emptyFn,
39205     iframePad:3,
39206     hideMode:'offsets',
39207     
39208     defaultAutoCreate : { // modified by initCompnoent..
39209         tag: "textarea",
39210         style:"width:500px;height:300px;",
39211         autocomplete: "off"
39212     },
39213
39214     // private
39215     initComponent : function(){
39216         this.addEvents({
39217             /**
39218              * @event initialize
39219              * Fires when the editor is fully initialized (including the iframe)
39220              * @param {HtmlEditor} this
39221              */
39222             initialize: true,
39223             /**
39224              * @event activate
39225              * Fires when the editor is first receives the focus. Any insertion must wait
39226              * until after this event.
39227              * @param {HtmlEditor} this
39228              */
39229             activate: true,
39230              /**
39231              * @event beforesync
39232              * Fires before the textarea is updated with content from the editor iframe. Return false
39233              * to cancel the sync.
39234              * @param {HtmlEditor} this
39235              * @param {String} html
39236              */
39237             beforesync: true,
39238              /**
39239              * @event beforepush
39240              * Fires before the iframe editor is updated with content from the textarea. Return false
39241              * to cancel the push.
39242              * @param {HtmlEditor} this
39243              * @param {String} html
39244              */
39245             beforepush: true,
39246              /**
39247              * @event sync
39248              * Fires when the textarea is updated with content from the editor iframe.
39249              * @param {HtmlEditor} this
39250              * @param {String} html
39251              */
39252             sync: true,
39253              /**
39254              * @event push
39255              * Fires when the iframe editor is updated with content from the textarea.
39256              * @param {HtmlEditor} this
39257              * @param {String} html
39258              */
39259             push: true,
39260              /**
39261              * @event editmodechange
39262              * Fires when the editor switches edit modes
39263              * @param {HtmlEditor} this
39264              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
39265              */
39266             editmodechange: true,
39267             /**
39268              * @event editorevent
39269              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
39270              * @param {HtmlEditor} this
39271              */
39272             editorevent: true
39273         });
39274         this.defaultAutoCreate =  {
39275             tag: "textarea",
39276             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
39277             autocomplete: "off"
39278         };
39279     },
39280
39281     /**
39282      * Protected method that will not generally be called directly. It
39283      * is called when the editor creates its toolbar. Override this method if you need to
39284      * add custom toolbar buttons.
39285      * @param {HtmlEditor} editor
39286      */
39287     createToolbar : function(editor){
39288         if (!editor.toolbars || !editor.toolbars.length) {
39289             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
39290         }
39291         
39292         for (var i =0 ; i < editor.toolbars.length;i++) {
39293             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
39294             editor.toolbars[i].init(editor);
39295         }
39296          
39297         
39298     },
39299
39300     /**
39301      * Protected method that will not generally be called directly. It
39302      * is called when the editor initializes the iframe with HTML contents. Override this method if you
39303      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
39304      */
39305     getDocMarkup : function(){
39306         // body styles..
39307         var st = '';
39308         if (this.stylesheets === false) {
39309             
39310             Roo.get(document.head).select('style').each(function(node) {
39311                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39312             });
39313             
39314             Roo.get(document.head).select('link').each(function(node) { 
39315                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
39316             });
39317             
39318         } else if (!this.stylesheets.length) {
39319                 // simple..
39320                 st = '<style type="text/css">' +
39321                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39322                    '</style>';
39323         } else {
39324             Roo.each(this.stylesheets, function(s) {
39325                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
39326             });
39327             
39328         }
39329         
39330         st +=  '<style type="text/css">' +
39331             'IMG { cursor: pointer } ' +
39332         '</style>';
39333
39334         
39335         return '<html><head>' + st  +
39336             //<style type="text/css">' +
39337             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
39338             //'</style>' +
39339             ' </head><body class="roo-htmleditor-body"></body></html>';
39340     },
39341
39342     // private
39343     onRender : function(ct, position)
39344     {
39345         var _t = this;
39346         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
39347         this.el.dom.style.border = '0 none';
39348         this.el.dom.setAttribute('tabIndex', -1);
39349         this.el.addClass('x-hidden');
39350         if(Roo.isIE){ // fix IE 1px bogus margin
39351             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
39352         }
39353         this.wrap = this.el.wrap({
39354             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
39355         });
39356         
39357         if (this.resizable) {
39358             this.resizeEl = new Roo.Resizable(this.wrap, {
39359                 pinned : true,
39360                 wrap: true,
39361                 dynamic : true,
39362                 minHeight : this.height,
39363                 height: this.height,
39364                 handles : this.resizable,
39365                 width: this.width,
39366                 listeners : {
39367                     resize : function(r, w, h) {
39368                         _t.onResize(w,h); // -something
39369                     }
39370                 }
39371             });
39372             
39373         }
39374
39375         this.frameId = Roo.id();
39376         
39377         this.createToolbar(this);
39378         
39379       
39380         
39381         var iframe = this.wrap.createChild({
39382             tag: 'iframe',
39383             id: this.frameId,
39384             name: this.frameId,
39385             frameBorder : 'no',
39386             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
39387         }, this.el
39388         );
39389         
39390        // console.log(iframe);
39391         //this.wrap.dom.appendChild(iframe);
39392
39393         this.iframe = iframe.dom;
39394
39395          this.assignDocWin();
39396         
39397         this.doc.designMode = 'on';
39398        
39399         this.doc.open();
39400         this.doc.write(this.getDocMarkup());
39401         this.doc.close();
39402
39403         
39404         var task = { // must defer to wait for browser to be ready
39405             run : function(){
39406                 //console.log("run task?" + this.doc.readyState);
39407                 this.assignDocWin();
39408                 if(this.doc.body || this.doc.readyState == 'complete'){
39409                     try {
39410                         this.doc.designMode="on";
39411                     } catch (e) {
39412                         return;
39413                     }
39414                     Roo.TaskMgr.stop(task);
39415                     this.initEditor.defer(10, this);
39416                 }
39417             },
39418             interval : 10,
39419             duration:10000,
39420             scope: this
39421         };
39422         Roo.TaskMgr.start(task);
39423
39424         if(!this.width){
39425             this.setSize(this.wrap.getSize());
39426         }
39427         if (this.resizeEl) {
39428             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
39429             // should trigger onReize..
39430         }
39431     },
39432
39433     // private
39434     onResize : function(w, h)
39435     {
39436         //Roo.log('resize: ' +w + ',' + h );
39437         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
39438         if(this.el && this.iframe){
39439             if(typeof w == 'number'){
39440                 var aw = w - this.wrap.getFrameWidth('lr');
39441                 this.el.setWidth(this.adjustWidth('textarea', aw));
39442                 this.iframe.style.width = aw + 'px';
39443             }
39444             if(typeof h == 'number'){
39445                 var tbh = 0;
39446                 for (var i =0; i < this.toolbars.length;i++) {
39447                     // fixme - ask toolbars for heights?
39448                     tbh += this.toolbars[i].tb.el.getHeight();
39449                     if (this.toolbars[i].footer) {
39450                         tbh += this.toolbars[i].footer.el.getHeight();
39451                     }
39452                 }
39453                 
39454                 
39455                 
39456                 
39457                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
39458                 ah -= 5; // knock a few pixes off for look..
39459                 this.el.setHeight(this.adjustWidth('textarea', ah));
39460                 this.iframe.style.height = ah + 'px';
39461                 if(this.doc){
39462                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
39463                 }
39464             }
39465         }
39466     },
39467
39468     /**
39469      * Toggles the editor between standard and source edit mode.
39470      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
39471      */
39472     toggleSourceEdit : function(sourceEditMode){
39473         
39474         this.sourceEditMode = sourceEditMode === true;
39475         
39476         if(this.sourceEditMode){
39477           
39478             this.syncValue();
39479             this.iframe.className = 'x-hidden';
39480             this.el.removeClass('x-hidden');
39481             this.el.dom.removeAttribute('tabIndex');
39482             this.el.focus();
39483         }else{
39484              
39485             this.pushValue();
39486             this.iframe.className = '';
39487             this.el.addClass('x-hidden');
39488             this.el.dom.setAttribute('tabIndex', -1);
39489             this.deferFocus();
39490         }
39491         this.setSize(this.wrap.getSize());
39492         this.fireEvent('editmodechange', this, this.sourceEditMode);
39493     },
39494
39495     // private used internally
39496     createLink : function(){
39497         var url = prompt(this.createLinkText, this.defaultLinkValue);
39498         if(url && url != 'http:/'+'/'){
39499             this.relayCmd('createlink', url);
39500         }
39501     },
39502
39503     // private (for BoxComponent)
39504     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39505
39506     // private (for BoxComponent)
39507     getResizeEl : function(){
39508         return this.wrap;
39509     },
39510
39511     // private (for BoxComponent)
39512     getPositionEl : function(){
39513         return this.wrap;
39514     },
39515
39516     // private
39517     initEvents : function(){
39518         this.originalValue = this.getValue();
39519     },
39520
39521     /**
39522      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39523      * @method
39524      */
39525     markInvalid : Roo.emptyFn,
39526     /**
39527      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39528      * @method
39529      */
39530     clearInvalid : Roo.emptyFn,
39531
39532     setValue : function(v){
39533         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39534         this.pushValue();
39535     },
39536
39537     /**
39538      * Protected method that will not generally be called directly. If you need/want
39539      * custom HTML cleanup, this is the method you should override.
39540      * @param {String} html The HTML to be cleaned
39541      * return {String} The cleaned HTML
39542      */
39543     cleanHtml : function(html){
39544         html = String(html);
39545         if(html.length > 5){
39546             if(Roo.isSafari){ // strip safari nonsense
39547                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39548             }
39549         }
39550         if(html == '&nbsp;'){
39551             html = '';
39552         }
39553         return html;
39554     },
39555
39556     /**
39557      * Protected method that will not generally be called directly. Syncs the contents
39558      * of the editor iframe with the textarea.
39559      */
39560     syncValue : function(){
39561         if(this.initialized){
39562             var bd = (this.doc.body || this.doc.documentElement);
39563             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39564             var html = bd.innerHTML;
39565             if(Roo.isSafari){
39566                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39567                 var m = bs.match(/text-align:(.*?);/i);
39568                 if(m && m[1]){
39569                     html = '<div style="'+m[0]+'">' + html + '</div>';
39570                 }
39571             }
39572             html = this.cleanHtml(html);
39573             // fix up the special chars..
39574             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39575                 return "&#"+b.charCodeAt()+";" 
39576             });
39577             if(this.fireEvent('beforesync', this, html) !== false){
39578                 this.el.dom.value = html;
39579                 this.fireEvent('sync', this, html);
39580             }
39581         }
39582     },
39583
39584     /**
39585      * Protected method that will not generally be called directly. Pushes the value of the textarea
39586      * into the iframe editor.
39587      */
39588     pushValue : function(){
39589         if(this.initialized){
39590             var v = this.el.dom.value;
39591             if(v.length < 1){
39592                 v = '&#160;';
39593             }
39594             
39595             if(this.fireEvent('beforepush', this, v) !== false){
39596                 var d = (this.doc.body || this.doc.documentElement);
39597                 d.innerHTML = v;
39598                 this.cleanUpPaste();
39599                 this.el.dom.value = d.innerHTML;
39600                 this.fireEvent('push', this, v);
39601             }
39602         }
39603     },
39604
39605     // private
39606     deferFocus : function(){
39607         this.focus.defer(10, this);
39608     },
39609
39610     // doc'ed in Field
39611     focus : function(){
39612         if(this.win && !this.sourceEditMode){
39613             this.win.focus();
39614         }else{
39615             this.el.focus();
39616         }
39617     },
39618     
39619     assignDocWin: function()
39620     {
39621         var iframe = this.iframe;
39622         
39623          if(Roo.isIE){
39624             this.doc = iframe.contentWindow.document;
39625             this.win = iframe.contentWindow;
39626         } else {
39627             if (!Roo.get(this.frameId)) {
39628                 return;
39629             }
39630             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39631             this.win = Roo.get(this.frameId).dom.contentWindow;
39632         }
39633     },
39634     
39635     // private
39636     initEditor : function(){
39637         //console.log("INIT EDITOR");
39638         this.assignDocWin();
39639         
39640         
39641         
39642         this.doc.designMode="on";
39643         this.doc.open();
39644         this.doc.write(this.getDocMarkup());
39645         this.doc.close();
39646         
39647         var dbody = (this.doc.body || this.doc.documentElement);
39648         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39649         // this copies styles from the containing element into thsi one..
39650         // not sure why we need all of this..
39651         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39652         ss['background-attachment'] = 'fixed'; // w3c
39653         dbody.bgProperties = 'fixed'; // ie
39654         Roo.DomHelper.applyStyles(dbody, ss);
39655         Roo.EventManager.on(this.doc, {
39656             //'mousedown': this.onEditorEvent,
39657             'mouseup': this.onEditorEvent,
39658             'dblclick': this.onEditorEvent,
39659             'click': this.onEditorEvent,
39660             'keyup': this.onEditorEvent,
39661             buffer:100,
39662             scope: this
39663         });
39664         if(Roo.isGecko){
39665             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39666         }
39667         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39668             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39669         }
39670         this.initialized = true;
39671
39672         this.fireEvent('initialize', this);
39673         this.pushValue();
39674     },
39675
39676     // private
39677     onDestroy : function(){
39678         
39679         
39680         
39681         if(this.rendered){
39682             
39683             for (var i =0; i < this.toolbars.length;i++) {
39684                 // fixme - ask toolbars for heights?
39685                 this.toolbars[i].onDestroy();
39686             }
39687             
39688             this.wrap.dom.innerHTML = '';
39689             this.wrap.remove();
39690         }
39691     },
39692
39693     // private
39694     onFirstFocus : function(){
39695         
39696         this.assignDocWin();
39697         
39698         
39699         this.activated = true;
39700         for (var i =0; i < this.toolbars.length;i++) {
39701             this.toolbars[i].onFirstFocus();
39702         }
39703        
39704         if(Roo.isGecko){ // prevent silly gecko errors
39705             this.win.focus();
39706             var s = this.win.getSelection();
39707             if(!s.focusNode || s.focusNode.nodeType != 3){
39708                 var r = s.getRangeAt(0);
39709                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39710                 r.collapse(true);
39711                 this.deferFocus();
39712             }
39713             try{
39714                 this.execCmd('useCSS', true);
39715                 this.execCmd('styleWithCSS', false);
39716             }catch(e){}
39717         }
39718         this.fireEvent('activate', this);
39719     },
39720
39721     // private
39722     adjustFont: function(btn){
39723         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39724         //if(Roo.isSafari){ // safari
39725         //    adjust *= 2;
39726        // }
39727         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39728         if(Roo.isSafari){ // safari
39729             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39730             v =  (v < 10) ? 10 : v;
39731             v =  (v > 48) ? 48 : v;
39732             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39733             
39734         }
39735         
39736         
39737         v = Math.max(1, v+adjust);
39738         
39739         this.execCmd('FontSize', v  );
39740     },
39741
39742     onEditorEvent : function(e){
39743         this.fireEvent('editorevent', this, e);
39744       //  this.updateToolbar();
39745         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39746     },
39747
39748     insertTag : function(tg)
39749     {
39750         // could be a bit smarter... -> wrap the current selected tRoo..
39751         
39752         this.execCmd("formatblock",   tg);
39753         
39754     },
39755     
39756     insertText : function(txt)
39757     {
39758         
39759         
39760         range = this.createRange();
39761         range.deleteContents();
39762                //alert(Sender.getAttribute('label'));
39763                
39764         range.insertNode(this.doc.createTextNode(txt));
39765     } ,
39766     
39767     // private
39768     relayBtnCmd : function(btn){
39769         this.relayCmd(btn.cmd);
39770     },
39771
39772     /**
39773      * Executes a Midas editor command on the editor document and performs necessary focus and
39774      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39775      * @param {String} cmd The Midas command
39776      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39777      */
39778     relayCmd : function(cmd, value){
39779         this.win.focus();
39780         this.execCmd(cmd, value);
39781         this.fireEvent('editorevent', this);
39782         //this.updateToolbar();
39783         this.deferFocus();
39784     },
39785
39786     /**
39787      * Executes a Midas editor command directly on the editor document.
39788      * For visual commands, you should use {@link #relayCmd} instead.
39789      * <b>This should only be called after the editor is initialized.</b>
39790      * @param {String} cmd The Midas command
39791      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39792      */
39793     execCmd : function(cmd, value){
39794         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39795         this.syncValue();
39796     },
39797  
39798  
39799    
39800     /**
39801      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39802      * to insert tRoo.
39803      * @param {String} text | dom node.. 
39804      */
39805     insertAtCursor : function(text)
39806     {
39807         
39808         
39809         
39810         if(!this.activated){
39811             return;
39812         }
39813         /*
39814         if(Roo.isIE){
39815             this.win.focus();
39816             var r = this.doc.selection.createRange();
39817             if(r){
39818                 r.collapse(true);
39819                 r.pasteHTML(text);
39820                 this.syncValue();
39821                 this.deferFocus();
39822             
39823             }
39824             return;
39825         }
39826         */
39827         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39828             this.win.focus();
39829             
39830             
39831             // from jquery ui (MIT licenced)
39832             var range, node;
39833             var win = this.win;
39834             
39835             if (win.getSelection && win.getSelection().getRangeAt) {
39836                 range = win.getSelection().getRangeAt(0);
39837                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39838                 range.insertNode(node);
39839             } else if (win.document.selection && win.document.selection.createRange) {
39840                 // no firefox support
39841                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39842                 win.document.selection.createRange().pasteHTML(txt);
39843             } else {
39844                 // no firefox support
39845                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39846                 this.execCmd('InsertHTML', txt);
39847             } 
39848             
39849             this.syncValue();
39850             
39851             this.deferFocus();
39852         }
39853     },
39854  // private
39855     mozKeyPress : function(e){
39856         if(e.ctrlKey){
39857             var c = e.getCharCode(), cmd;
39858           
39859             if(c > 0){
39860                 c = String.fromCharCode(c).toLowerCase();
39861                 switch(c){
39862                     case 'b':
39863                         cmd = 'bold';
39864                         break;
39865                     case 'i':
39866                         cmd = 'italic';
39867                         break;
39868                     
39869                     case 'u':
39870                         cmd = 'underline';
39871                         break;
39872                     
39873                     case 'v':
39874                         this.cleanUpPaste.defer(100, this);
39875                         return;
39876                         
39877                 }
39878                 if(cmd){
39879                     this.win.focus();
39880                     this.execCmd(cmd);
39881                     this.deferFocus();
39882                     e.preventDefault();
39883                 }
39884                 
39885             }
39886         }
39887     },
39888
39889     // private
39890     fixKeys : function(){ // load time branching for fastest keydown performance
39891         if(Roo.isIE){
39892             return function(e){
39893                 var k = e.getKey(), r;
39894                 if(k == e.TAB){
39895                     e.stopEvent();
39896                     r = this.doc.selection.createRange();
39897                     if(r){
39898                         r.collapse(true);
39899                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39900                         this.deferFocus();
39901                     }
39902                     return;
39903                 }
39904                 
39905                 if(k == e.ENTER){
39906                     r = this.doc.selection.createRange();
39907                     if(r){
39908                         var target = r.parentElement();
39909                         if(!target || target.tagName.toLowerCase() != 'li'){
39910                             e.stopEvent();
39911                             r.pasteHTML('<br />');
39912                             r.collapse(false);
39913                             r.select();
39914                         }
39915                     }
39916                 }
39917                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39918                     this.cleanUpPaste.defer(100, this);
39919                     return;
39920                 }
39921                 
39922                 
39923             };
39924         }else if(Roo.isOpera){
39925             return function(e){
39926                 var k = e.getKey();
39927                 if(k == e.TAB){
39928                     e.stopEvent();
39929                     this.win.focus();
39930                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39931                     this.deferFocus();
39932                 }
39933                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39934                     this.cleanUpPaste.defer(100, this);
39935                     return;
39936                 }
39937                 
39938             };
39939         }else if(Roo.isSafari){
39940             return function(e){
39941                 var k = e.getKey();
39942                 
39943                 if(k == e.TAB){
39944                     e.stopEvent();
39945                     this.execCmd('InsertText','\t');
39946                     this.deferFocus();
39947                     return;
39948                 }
39949                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39950                     this.cleanUpPaste.defer(100, this);
39951                     return;
39952                 }
39953                 
39954              };
39955         }
39956     }(),
39957     
39958     getAllAncestors: function()
39959     {
39960         var p = this.getSelectedNode();
39961         var a = [];
39962         if (!p) {
39963             a.push(p); // push blank onto stack..
39964             p = this.getParentElement();
39965         }
39966         
39967         
39968         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39969             a.push(p);
39970             p = p.parentNode;
39971         }
39972         a.push(this.doc.body);
39973         return a;
39974     },
39975     lastSel : false,
39976     lastSelNode : false,
39977     
39978     
39979     getSelection : function() 
39980     {
39981         this.assignDocWin();
39982         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39983     },
39984     
39985     getSelectedNode: function() 
39986     {
39987         // this may only work on Gecko!!!
39988         
39989         // should we cache this!!!!
39990         
39991         
39992         
39993          
39994         var range = this.createRange(this.getSelection()).cloneRange();
39995         
39996         if (Roo.isIE) {
39997             var parent = range.parentElement();
39998             while (true) {
39999                 var testRange = range.duplicate();
40000                 testRange.moveToElementText(parent);
40001                 if (testRange.inRange(range)) {
40002                     break;
40003                 }
40004                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
40005                     break;
40006                 }
40007                 parent = parent.parentElement;
40008             }
40009             return parent;
40010         }
40011         
40012         // is ancestor a text element.
40013         var ac =  range.commonAncestorContainer;
40014         if (ac.nodeType == 3) {
40015             ac = ac.parentNode;
40016         }
40017         
40018         var ar = ac.childNodes;
40019          
40020         var nodes = [];
40021         var other_nodes = [];
40022         var has_other_nodes = false;
40023         for (var i=0;i<ar.length;i++) {
40024             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
40025                 continue;
40026             }
40027             // fullly contained node.
40028             
40029             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
40030                 nodes.push(ar[i]);
40031                 continue;
40032             }
40033             
40034             // probably selected..
40035             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
40036                 other_nodes.push(ar[i]);
40037                 continue;
40038             }
40039             // outer..
40040             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
40041                 continue;
40042             }
40043             
40044             
40045             has_other_nodes = true;
40046         }
40047         if (!nodes.length && other_nodes.length) {
40048             nodes= other_nodes;
40049         }
40050         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
40051             return false;
40052         }
40053         
40054         return nodes[0];
40055     },
40056     createRange: function(sel)
40057     {
40058         // this has strange effects when using with 
40059         // top toolbar - not sure if it's a great idea.
40060         //this.editor.contentWindow.focus();
40061         if (typeof sel != "undefined") {
40062             try {
40063                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
40064             } catch(e) {
40065                 return this.doc.createRange();
40066             }
40067         } else {
40068             return this.doc.createRange();
40069         }
40070     },
40071     getParentElement: function()
40072     {
40073         
40074         this.assignDocWin();
40075         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
40076         
40077         var range = this.createRange(sel);
40078          
40079         try {
40080             var p = range.commonAncestorContainer;
40081             while (p.nodeType == 3) { // text node
40082                 p = p.parentNode;
40083             }
40084             return p;
40085         } catch (e) {
40086             return null;
40087         }
40088     
40089     },
40090     /***
40091      *
40092      * Range intersection.. the hard stuff...
40093      *  '-1' = before
40094      *  '0' = hits..
40095      *  '1' = after.
40096      *         [ -- selected range --- ]
40097      *   [fail]                        [fail]
40098      *
40099      *    basically..
40100      *      if end is before start or  hits it. fail.
40101      *      if start is after end or hits it fail.
40102      *
40103      *   if either hits (but other is outside. - then it's not 
40104      *   
40105      *    
40106      **/
40107     
40108     
40109     // @see http://www.thismuchiknow.co.uk/?p=64.
40110     rangeIntersectsNode : function(range, node)
40111     {
40112         var nodeRange = node.ownerDocument.createRange();
40113         try {
40114             nodeRange.selectNode(node);
40115         } catch (e) {
40116             nodeRange.selectNodeContents(node);
40117         }
40118     
40119         var rangeStartRange = range.cloneRange();
40120         rangeStartRange.collapse(true);
40121     
40122         var rangeEndRange = range.cloneRange();
40123         rangeEndRange.collapse(false);
40124     
40125         var nodeStartRange = nodeRange.cloneRange();
40126         nodeStartRange.collapse(true);
40127     
40128         var nodeEndRange = nodeRange.cloneRange();
40129         nodeEndRange.collapse(false);
40130     
40131         return rangeStartRange.compareBoundaryPoints(
40132                  Range.START_TO_START, nodeEndRange) == -1 &&
40133                rangeEndRange.compareBoundaryPoints(
40134                  Range.START_TO_START, nodeStartRange) == 1;
40135         
40136          
40137     },
40138     rangeCompareNode : function(range, node)
40139     {
40140         var nodeRange = node.ownerDocument.createRange();
40141         try {
40142             nodeRange.selectNode(node);
40143         } catch (e) {
40144             nodeRange.selectNodeContents(node);
40145         }
40146         
40147         
40148         range.collapse(true);
40149     
40150         nodeRange.collapse(true);
40151      
40152         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
40153         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
40154          
40155         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
40156         
40157         var nodeIsBefore   =  ss == 1;
40158         var nodeIsAfter    = ee == -1;
40159         
40160         if (nodeIsBefore && nodeIsAfter)
40161             return 0; // outer
40162         if (!nodeIsBefore && nodeIsAfter)
40163             return 1; //right trailed.
40164         
40165         if (nodeIsBefore && !nodeIsAfter)
40166             return 2;  // left trailed.
40167         // fully contined.
40168         return 3;
40169     },
40170
40171     // private? - in a new class?
40172     cleanUpPaste :  function()
40173     {
40174         // cleans up the whole document..
40175          Roo.log('cleanuppaste');
40176         this.cleanUpChildren(this.doc.body);
40177         var clean = this.cleanWordChars(this.doc.body.innerHTML);
40178         if (clean != this.doc.body.innerHTML) {
40179             this.doc.body.innerHTML = clean;
40180         }
40181         
40182     },
40183     
40184     cleanWordChars : function(input) {
40185         var he = Roo.form.HtmlEditor;
40186     
40187         var output = input;
40188         Roo.each(he.swapCodes, function(sw) { 
40189         
40190             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
40191             output = output.replace(swapper, sw[1]);
40192         });
40193         return output;
40194     },
40195     
40196     
40197     cleanUpChildren : function (n)
40198     {
40199         if (!n.childNodes.length) {
40200             return;
40201         }
40202         for (var i = n.childNodes.length-1; i > -1 ; i--) {
40203            this.cleanUpChild(n.childNodes[i]);
40204         }
40205     },
40206     
40207     
40208         
40209     
40210     cleanUpChild : function (node)
40211     {
40212         //console.log(node);
40213         if (node.nodeName == "#text") {
40214             // clean up silly Windows -- stuff?
40215             return; 
40216         }
40217         if (node.nodeName == "#comment") {
40218             node.parentNode.removeChild(node);
40219             // clean up silly Windows -- stuff?
40220             return; 
40221         }
40222         
40223         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
40224             // remove node.
40225             node.parentNode.removeChild(node);
40226             return;
40227             
40228         }
40229         
40230         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
40231         
40232         // remove <a name=....> as rendering on yahoo mailer is bored with this.
40233         
40234         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
40235             remove_keep_children = true;
40236         }
40237         
40238         if (remove_keep_children) {
40239             this.cleanUpChildren(node);
40240             // inserts everything just before this node...
40241             while (node.childNodes.length) {
40242                 var cn = node.childNodes[0];
40243                 node.removeChild(cn);
40244                 node.parentNode.insertBefore(cn, node);
40245             }
40246             node.parentNode.removeChild(node);
40247             return;
40248         }
40249         
40250         if (!node.attributes || !node.attributes.length) {
40251             this.cleanUpChildren(node);
40252             return;
40253         }
40254         
40255         function cleanAttr(n,v)
40256         {
40257             
40258             if (v.match(/^\./) || v.match(/^\//)) {
40259                 return;
40260             }
40261             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
40262                 return;
40263             }
40264             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
40265             node.removeAttribute(n);
40266             
40267         }
40268         
40269         function cleanStyle(n,v)
40270         {
40271             if (v.match(/expression/)) { //XSS?? should we even bother..
40272                 node.removeAttribute(n);
40273                 return;
40274             }
40275             
40276             
40277             var parts = v.split(/;/);
40278             Roo.each(parts, function(p) {
40279                 p = p.replace(/\s+/g,'');
40280                 if (!p.length) {
40281                     return true;
40282                 }
40283                 var l = p.split(':').shift().replace(/\s+/g,'');
40284                 
40285                 // only allow 'c whitelisted system attributes'
40286                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
40287                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
40288                     node.removeAttribute(n);
40289                     return false;
40290                 }
40291                 return true;
40292             });
40293             
40294             
40295         }
40296         
40297         
40298         for (var i = node.attributes.length-1; i > -1 ; i--) {
40299             var a = node.attributes[i];
40300             //console.log(a);
40301             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
40302                 node.removeAttribute(a.name);
40303                 return;
40304             }
40305             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
40306                 cleanAttr(a.name,a.value); // fixme..
40307                 return;
40308             }
40309             if (a.name == 'style') {
40310                 cleanStyle(a.name,a.value);
40311             }
40312             /// clean up MS crap..
40313             // tecnically this should be a list of valid class'es..
40314             
40315             
40316             if (a.name == 'class') {
40317                 if (a.value.match(/^Mso/)) {
40318                     node.className = '';
40319                 }
40320                 
40321                 if (a.value.match(/body/)) {
40322                     node.className = '';
40323                 }
40324             }
40325             
40326             // style cleanup!?
40327             // class cleanup?
40328             
40329         }
40330         
40331         
40332         this.cleanUpChildren(node);
40333         
40334         
40335     }
40336     
40337     
40338     // hide stuff that is not compatible
40339     /**
40340      * @event blur
40341      * @hide
40342      */
40343     /**
40344      * @event change
40345      * @hide
40346      */
40347     /**
40348      * @event focus
40349      * @hide
40350      */
40351     /**
40352      * @event specialkey
40353      * @hide
40354      */
40355     /**
40356      * @cfg {String} fieldClass @hide
40357      */
40358     /**
40359      * @cfg {String} focusClass @hide
40360      */
40361     /**
40362      * @cfg {String} autoCreate @hide
40363      */
40364     /**
40365      * @cfg {String} inputType @hide
40366      */
40367     /**
40368      * @cfg {String} invalidClass @hide
40369      */
40370     /**
40371      * @cfg {String} invalidText @hide
40372      */
40373     /**
40374      * @cfg {String} msgFx @hide
40375      */
40376     /**
40377      * @cfg {String} validateOnBlur @hide
40378      */
40379 });
40380
40381 Roo.form.HtmlEditor.white = [
40382         'area', 'br', 'img', 'input', 'hr', 'wbr',
40383         
40384        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
40385        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
40386        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
40387        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
40388        'table',   'ul',         'xmp', 
40389        
40390        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
40391       'thead',   'tr', 
40392      
40393       'dir', 'menu', 'ol', 'ul', 'dl',
40394        
40395       'embed',  'object'
40396 ];
40397
40398
40399 Roo.form.HtmlEditor.black = [
40400     //    'embed',  'object', // enable - backend responsiblity to clean thiese
40401         'applet', // 
40402         'base',   'basefont', 'bgsound', 'blink',  'body', 
40403         'frame',  'frameset', 'head',    'html',   'ilayer', 
40404         'iframe', 'layer',  'link',     'meta',    'object',   
40405         'script', 'style' ,'title',  'xml' // clean later..
40406 ];
40407 Roo.form.HtmlEditor.clean = [
40408     'script', 'style', 'title', 'xml'
40409 ];
40410 Roo.form.HtmlEditor.remove = [
40411     'font'
40412 ];
40413 // attributes..
40414
40415 Roo.form.HtmlEditor.ablack = [
40416     'on'
40417 ];
40418     
40419 Roo.form.HtmlEditor.aclean = [ 
40420     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
40421 ];
40422
40423 // protocols..
40424 Roo.form.HtmlEditor.pwhite= [
40425         'http',  'https',  'mailto'
40426 ];
40427
40428 // white listed style attributes.
40429 Roo.form.HtmlEditor.cwhite= [
40430         'text-align',
40431         'font-size'
40432 ];
40433
40434
40435 Roo.form.HtmlEditor.swapCodes   =[ 
40436     [    8211, "--" ], 
40437     [    8212, "--" ], 
40438     [    8216,  "'" ],  
40439     [    8217, "'" ],  
40440     [    8220, '"' ],  
40441     [    8221, '"' ],  
40442     [    8226, "*" ],  
40443     [    8230, "..." ]
40444 ]; 
40445
40446     // <script type="text/javascript">
40447 /*
40448  * Based on
40449  * Ext JS Library 1.1.1
40450  * Copyright(c) 2006-2007, Ext JS, LLC.
40451  *  
40452  
40453  */
40454
40455 /**
40456  * @class Roo.form.HtmlEditorToolbar1
40457  * Basic Toolbar
40458  * 
40459  * Usage:
40460  *
40461  new Roo.form.HtmlEditor({
40462     ....
40463     toolbars : [
40464         new Roo.form.HtmlEditorToolbar1({
40465             disable : { fonts: 1 , format: 1, ..., ... , ...],
40466             btns : [ .... ]
40467         })
40468     }
40469      
40470  * 
40471  * @cfg {Object} disable List of elements to disable..
40472  * @cfg {Array} btns List of additional buttons.
40473  * 
40474  * 
40475  * NEEDS Extra CSS? 
40476  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
40477  */
40478  
40479 Roo.form.HtmlEditor.ToolbarStandard = function(config)
40480 {
40481     
40482     Roo.apply(this, config);
40483     
40484     // default disabled, based on 'good practice'..
40485     this.disable = this.disable || {};
40486     Roo.applyIf(this.disable, {
40487         fontSize : true,
40488         colors : true,
40489         specialElements : true
40490     });
40491     
40492     
40493     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40494     // dont call parent... till later.
40495 }
40496
40497 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
40498     
40499     tb: false,
40500     
40501     rendered: false,
40502     
40503     editor : false,
40504     /**
40505      * @cfg {Object} disable  List of toolbar elements to disable
40506          
40507      */
40508     disable : false,
40509       /**
40510      * @cfg {Array} fontFamilies An array of available font families
40511      */
40512     fontFamilies : [
40513         'Arial',
40514         'Courier New',
40515         'Tahoma',
40516         'Times New Roman',
40517         'Verdana'
40518     ],
40519     
40520     specialChars : [
40521            "&#169;",
40522           "&#174;",     
40523           "&#8482;",    
40524           "&#163;" ,    
40525          // "&#8212;",    
40526           "&#8230;",    
40527           "&#247;" ,    
40528         //  "&#225;" ,     ?? a acute?
40529            "&#8364;"    , //Euro
40530        //   "&#8220;"    ,
40531         //  "&#8221;"    ,
40532         //  "&#8226;"    ,
40533           "&#176;"  //   , // degrees
40534
40535          // "&#233;"     , // e ecute
40536          // "&#250;"     , // u ecute?
40537     ],
40538     
40539     specialElements : [
40540         {
40541             text: "Insert Table",
40542             xtype: 'MenuItem',
40543             xns : Roo.Menu,
40544             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40545                 
40546         },
40547         {    
40548             text: "Insert Image",
40549             xtype: 'MenuItem',
40550             xns : Roo.Menu,
40551             ihtml : '<img src="about:blank"/>'
40552             
40553         }
40554         
40555          
40556     ],
40557     
40558     
40559     inputElements : [ 
40560             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40561             "input:submit", "input:button", "select", "textarea", "label" ],
40562     formats : [
40563         ["p"] ,  
40564         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40565         ["pre"],[ "code"], 
40566         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40567     ],
40568      /**
40569      * @cfg {String} defaultFont default font to use.
40570      */
40571     defaultFont: 'tahoma',
40572    
40573     fontSelect : false,
40574     
40575     
40576     formatCombo : false,
40577     
40578     init : function(editor)
40579     {
40580         this.editor = editor;
40581         
40582         
40583         var fid = editor.frameId;
40584         var etb = this;
40585         function btn(id, toggle, handler){
40586             var xid = fid + '-'+ id ;
40587             return {
40588                 id : xid,
40589                 cmd : id,
40590                 cls : 'x-btn-icon x-edit-'+id,
40591                 enableToggle:toggle !== false,
40592                 scope: editor, // was editor...
40593                 handler:handler||editor.relayBtnCmd,
40594                 clickEvent:'mousedown',
40595                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40596                 tabIndex:-1
40597             };
40598         }
40599         
40600         
40601         
40602         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40603         this.tb = tb;
40604          // stop form submits
40605         tb.el.on('click', function(e){
40606             e.preventDefault(); // what does this do?
40607         });
40608
40609         if(!this.disable.font && !Roo.isSafari){
40610             /* why no safari for fonts
40611             editor.fontSelect = tb.el.createChild({
40612                 tag:'select',
40613                 tabIndex: -1,
40614                 cls:'x-font-select',
40615                 html: editor.createFontOptions()
40616             });
40617             editor.fontSelect.on('change', function(){
40618                 var font = editor.fontSelect.dom.value;
40619                 editor.relayCmd('fontname', font);
40620                 editor.deferFocus();
40621             }, editor);
40622             tb.add(
40623                 editor.fontSelect.dom,
40624                 '-'
40625             );
40626             */
40627         };
40628         if(!this.disable.formats){
40629             this.formatCombo = new Roo.form.ComboBox({
40630                 store: new Roo.data.SimpleStore({
40631                     id : 'tag',
40632                     fields: ['tag'],
40633                     data : this.formats // from states.js
40634                 }),
40635                 blockFocus : true,
40636                 //autoCreate : {tag: "div",  size: "20"},
40637                 displayField:'tag',
40638                 typeAhead: false,
40639                 mode: 'local',
40640                 editable : false,
40641                 triggerAction: 'all',
40642                 emptyText:'Add tag',
40643                 selectOnFocus:true,
40644                 width:135,
40645                 listeners : {
40646                     'select': function(c, r, i) {
40647                         editor.insertTag(r.get('tag'));
40648                         editor.focus();
40649                     }
40650                 }
40651
40652             });
40653             tb.addField(this.formatCombo);
40654             
40655         }
40656         
40657         if(!this.disable.format){
40658             tb.add(
40659                 btn('bold'),
40660                 btn('italic'),
40661                 btn('underline')
40662             );
40663         };
40664         if(!this.disable.fontSize){
40665             tb.add(
40666                 '-',
40667                 
40668                 
40669                 btn('increasefontsize', false, editor.adjustFont),
40670                 btn('decreasefontsize', false, editor.adjustFont)
40671             );
40672         };
40673         
40674         
40675         if(!this.disable.colors){
40676             tb.add(
40677                 '-', {
40678                     id:editor.frameId +'-forecolor',
40679                     cls:'x-btn-icon x-edit-forecolor',
40680                     clickEvent:'mousedown',
40681                     tooltip: this.buttonTips['forecolor'] || undefined,
40682                     tabIndex:-1,
40683                     menu : new Roo.menu.ColorMenu({
40684                         allowReselect: true,
40685                         focus: Roo.emptyFn,
40686                         value:'000000',
40687                         plain:true,
40688                         selectHandler: function(cp, color){
40689                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40690                             editor.deferFocus();
40691                         },
40692                         scope: editor,
40693                         clickEvent:'mousedown'
40694                     })
40695                 }, {
40696                     id:editor.frameId +'backcolor',
40697                     cls:'x-btn-icon x-edit-backcolor',
40698                     clickEvent:'mousedown',
40699                     tooltip: this.buttonTips['backcolor'] || undefined,
40700                     tabIndex:-1,
40701                     menu : new Roo.menu.ColorMenu({
40702                         focus: Roo.emptyFn,
40703                         value:'FFFFFF',
40704                         plain:true,
40705                         allowReselect: true,
40706                         selectHandler: function(cp, color){
40707                             if(Roo.isGecko){
40708                                 editor.execCmd('useCSS', false);
40709                                 editor.execCmd('hilitecolor', color);
40710                                 editor.execCmd('useCSS', true);
40711                                 editor.deferFocus();
40712                             }else{
40713                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40714                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40715                                 editor.deferFocus();
40716                             }
40717                         },
40718                         scope:editor,
40719                         clickEvent:'mousedown'
40720                     })
40721                 }
40722             );
40723         };
40724         // now add all the items...
40725         
40726
40727         if(!this.disable.alignments){
40728             tb.add(
40729                 '-',
40730                 btn('justifyleft'),
40731                 btn('justifycenter'),
40732                 btn('justifyright')
40733             );
40734         };
40735
40736         //if(!Roo.isSafari){
40737             if(!this.disable.links){
40738                 tb.add(
40739                     '-',
40740                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40741                 );
40742             };
40743
40744             if(!this.disable.lists){
40745                 tb.add(
40746                     '-',
40747                     btn('insertorderedlist'),
40748                     btn('insertunorderedlist')
40749                 );
40750             }
40751             if(!this.disable.sourceEdit){
40752                 tb.add(
40753                     '-',
40754                     btn('sourceedit', true, function(btn){
40755                         this.toggleSourceEdit(btn.pressed);
40756                     })
40757                 );
40758             }
40759         //}
40760         
40761         var smenu = { };
40762         // special menu.. - needs to be tidied up..
40763         if (!this.disable.special) {
40764             smenu = {
40765                 text: "&#169;",
40766                 cls: 'x-edit-none',
40767                 
40768                 menu : {
40769                     items : []
40770                 }
40771             };
40772             for (var i =0; i < this.specialChars.length; i++) {
40773                 smenu.menu.items.push({
40774                     
40775                     html: this.specialChars[i],
40776                     handler: function(a,b) {
40777                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40778                         //editor.insertAtCursor(a.html);
40779                         
40780                     },
40781                     tabIndex:-1
40782                 });
40783             }
40784             
40785             
40786             tb.add(smenu);
40787             
40788             
40789         }
40790          
40791         if (!this.disable.specialElements) {
40792             var semenu = {
40793                 text: "Other;",
40794                 cls: 'x-edit-none',
40795                 menu : {
40796                     items : []
40797                 }
40798             };
40799             for (var i =0; i < this.specialElements.length; i++) {
40800                 semenu.menu.items.push(
40801                     Roo.apply({ 
40802                         handler: function(a,b) {
40803                             editor.insertAtCursor(this.ihtml);
40804                         }
40805                     }, this.specialElements[i])
40806                 );
40807                     
40808             }
40809             
40810             tb.add(semenu);
40811             
40812             
40813         }
40814          
40815         
40816         if (this.btns) {
40817             for(var i =0; i< this.btns.length;i++) {
40818                 var b = Roo.factory(this.btns[i],Roo.form);
40819                 b.cls =  'x-edit-none';
40820                 b.scope = editor;
40821                 tb.add(b);
40822             }
40823         
40824         }
40825         
40826         
40827         
40828         // disable everything...
40829         
40830         this.tb.items.each(function(item){
40831            if(item.id != editor.frameId+ '-sourceedit'){
40832                 item.disable();
40833             }
40834         });
40835         this.rendered = true;
40836         
40837         // the all the btns;
40838         editor.on('editorevent', this.updateToolbar, this);
40839         // other toolbars need to implement this..
40840         //editor.on('editmodechange', this.updateToolbar, this);
40841     },
40842     
40843     
40844     
40845     /**
40846      * Protected method that will not generally be called directly. It triggers
40847      * a toolbar update by reading the markup state of the current selection in the editor.
40848      */
40849     updateToolbar: function(){
40850
40851         if(!this.editor.activated){
40852             this.editor.onFirstFocus();
40853             return;
40854         }
40855
40856         var btns = this.tb.items.map, 
40857             doc = this.editor.doc,
40858             frameId = this.editor.frameId;
40859
40860         if(!this.disable.font && !Roo.isSafari){
40861             /*
40862             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40863             if(name != this.fontSelect.dom.value){
40864                 this.fontSelect.dom.value = name;
40865             }
40866             */
40867         }
40868         if(!this.disable.format){
40869             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40870             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40871             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40872         }
40873         if(!this.disable.alignments){
40874             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40875             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40876             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40877         }
40878         if(!Roo.isSafari && !this.disable.lists){
40879             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40880             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40881         }
40882         
40883         var ans = this.editor.getAllAncestors();
40884         if (this.formatCombo) {
40885             
40886             
40887             var store = this.formatCombo.store;
40888             this.formatCombo.setValue("");
40889             for (var i =0; i < ans.length;i++) {
40890                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40891                     // select it..
40892                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40893                     break;
40894                 }
40895             }
40896         }
40897         
40898         
40899         
40900         // hides menus... - so this cant be on a menu...
40901         Roo.menu.MenuMgr.hideAll();
40902
40903         //this.editorsyncValue();
40904     },
40905    
40906     
40907     createFontOptions : function(){
40908         var buf = [], fs = this.fontFamilies, ff, lc;
40909         for(var i = 0, len = fs.length; i< len; i++){
40910             ff = fs[i];
40911             lc = ff.toLowerCase();
40912             buf.push(
40913                 '<option value="',lc,'" style="font-family:',ff,';"',
40914                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40915                     ff,
40916                 '</option>'
40917             );
40918         }
40919         return buf.join('');
40920     },
40921     
40922     toggleSourceEdit : function(sourceEditMode){
40923         if(sourceEditMode === undefined){
40924             sourceEditMode = !this.sourceEditMode;
40925         }
40926         this.sourceEditMode = sourceEditMode === true;
40927         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40928         // just toggle the button?
40929         if(btn.pressed !== this.editor.sourceEditMode){
40930             btn.toggle(this.editor.sourceEditMode);
40931             return;
40932         }
40933         
40934         if(this.sourceEditMode){
40935             this.tb.items.each(function(item){
40936                 if(item.cmd != 'sourceedit'){
40937                     item.disable();
40938                 }
40939             });
40940           
40941         }else{
40942             if(this.initialized){
40943                 this.tb.items.each(function(item){
40944                     item.enable();
40945                 });
40946             }
40947             
40948         }
40949         // tell the editor that it's been pressed..
40950         this.editor.toggleSourceEdit(sourceEditMode);
40951        
40952     },
40953      /**
40954      * Object collection of toolbar tooltips for the buttons in the editor. The key
40955      * is the command id associated with that button and the value is a valid QuickTips object.
40956      * For example:
40957 <pre><code>
40958 {
40959     bold : {
40960         title: 'Bold (Ctrl+B)',
40961         text: 'Make the selected text bold.',
40962         cls: 'x-html-editor-tip'
40963     },
40964     italic : {
40965         title: 'Italic (Ctrl+I)',
40966         text: 'Make the selected text italic.',
40967         cls: 'x-html-editor-tip'
40968     },
40969     ...
40970 </code></pre>
40971     * @type Object
40972      */
40973     buttonTips : {
40974         bold : {
40975             title: 'Bold (Ctrl+B)',
40976             text: 'Make the selected text bold.',
40977             cls: 'x-html-editor-tip'
40978         },
40979         italic : {
40980             title: 'Italic (Ctrl+I)',
40981             text: 'Make the selected text italic.',
40982             cls: 'x-html-editor-tip'
40983         },
40984         underline : {
40985             title: 'Underline (Ctrl+U)',
40986             text: 'Underline the selected text.',
40987             cls: 'x-html-editor-tip'
40988         },
40989         increasefontsize : {
40990             title: 'Grow Text',
40991             text: 'Increase the font size.',
40992             cls: 'x-html-editor-tip'
40993         },
40994         decreasefontsize : {
40995             title: 'Shrink Text',
40996             text: 'Decrease the font size.',
40997             cls: 'x-html-editor-tip'
40998         },
40999         backcolor : {
41000             title: 'Text Highlight Color',
41001             text: 'Change the background color of the selected text.',
41002             cls: 'x-html-editor-tip'
41003         },
41004         forecolor : {
41005             title: 'Font Color',
41006             text: 'Change the color of the selected text.',
41007             cls: 'x-html-editor-tip'
41008         },
41009         justifyleft : {
41010             title: 'Align Text Left',
41011             text: 'Align text to the left.',
41012             cls: 'x-html-editor-tip'
41013         },
41014         justifycenter : {
41015             title: 'Center Text',
41016             text: 'Center text in the editor.',
41017             cls: 'x-html-editor-tip'
41018         },
41019         justifyright : {
41020             title: 'Align Text Right',
41021             text: 'Align text to the right.',
41022             cls: 'x-html-editor-tip'
41023         },
41024         insertunorderedlist : {
41025             title: 'Bullet List',
41026             text: 'Start a bulleted list.',
41027             cls: 'x-html-editor-tip'
41028         },
41029         insertorderedlist : {
41030             title: 'Numbered List',
41031             text: 'Start a numbered list.',
41032             cls: 'x-html-editor-tip'
41033         },
41034         createlink : {
41035             title: 'Hyperlink',
41036             text: 'Make the selected text a hyperlink.',
41037             cls: 'x-html-editor-tip'
41038         },
41039         sourceedit : {
41040             title: 'Source Edit',
41041             text: 'Switch to source editing mode.',
41042             cls: 'x-html-editor-tip'
41043         }
41044     },
41045     // private
41046     onDestroy : function(){
41047         if(this.rendered){
41048             
41049             this.tb.items.each(function(item){
41050                 if(item.menu){
41051                     item.menu.removeAll();
41052                     if(item.menu.el){
41053                         item.menu.el.destroy();
41054                     }
41055                 }
41056                 item.destroy();
41057             });
41058              
41059         }
41060     },
41061     onFirstFocus: function() {
41062         this.tb.items.each(function(item){
41063            item.enable();
41064         });
41065     }
41066 });
41067
41068
41069
41070
41071 // <script type="text/javascript">
41072 /*
41073  * Based on
41074  * Ext JS Library 1.1.1
41075  * Copyright(c) 2006-2007, Ext JS, LLC.
41076  *  
41077  
41078  */
41079
41080  
41081 /**
41082  * @class Roo.form.HtmlEditor.ToolbarContext
41083  * Context Toolbar
41084  * 
41085  * Usage:
41086  *
41087  new Roo.form.HtmlEditor({
41088     ....
41089     toolbars : [
41090         { xtype: 'ToolbarStandard', styles : {} }
41091         { xtype: 'ToolbarContext', disable : {} }
41092     ]
41093 })
41094
41095      
41096  * 
41097  * @config : {Object} disable List of elements to disable.. (not done yet.)
41098  * @config : {Object} styles  Map of styles available.
41099  * 
41100  */
41101
41102 Roo.form.HtmlEditor.ToolbarContext = function(config)
41103 {
41104     
41105     Roo.apply(this, config);
41106     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41107     // dont call parent... till later.
41108     this.styles = this.styles || {};
41109 }
41110 Roo.form.HtmlEditor.ToolbarContext.types = {
41111     'IMG' : {
41112         width : {
41113             title: "Width",
41114             width: 40
41115         },
41116         height:  {
41117             title: "Height",
41118             width: 40
41119         },
41120         align: {
41121             title: "Align",
41122             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
41123             width : 80
41124             
41125         },
41126         border: {
41127             title: "Border",
41128             width: 40
41129         },
41130         alt: {
41131             title: "Alt",
41132             width: 120
41133         },
41134         src : {
41135             title: "Src",
41136             width: 220
41137         }
41138         
41139     },
41140     'A' : {
41141         name : {
41142             title: "Name",
41143             width: 50
41144         },
41145         href:  {
41146             title: "Href",
41147             width: 220
41148         } // border?
41149         
41150     },
41151     'TABLE' : {
41152         rows : {
41153             title: "Rows",
41154             width: 20
41155         },
41156         cols : {
41157             title: "Cols",
41158             width: 20
41159         },
41160         width : {
41161             title: "Width",
41162             width: 40
41163         },
41164         height : {
41165             title: "Height",
41166             width: 40
41167         },
41168         border : {
41169             title: "Border",
41170             width: 20
41171         }
41172     },
41173     'TD' : {
41174         width : {
41175             title: "Width",
41176             width: 40
41177         },
41178         height : {
41179             title: "Height",
41180             width: 40
41181         },   
41182         align: {
41183             title: "Align",
41184             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
41185             width: 80
41186         },
41187         valign: {
41188             title: "Valign",
41189             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
41190             width: 80
41191         },
41192         colspan: {
41193             title: "Colspan",
41194             width: 20
41195             
41196         }
41197     },
41198     'INPUT' : {
41199         name : {
41200             title: "name",
41201             width: 120
41202         },
41203         value : {
41204             title: "Value",
41205             width: 120
41206         },
41207         width : {
41208             title: "Width",
41209             width: 40
41210         }
41211     },
41212     'LABEL' : {
41213         'for' : {
41214             title: "For",
41215             width: 120
41216         }
41217     },
41218     'TEXTAREA' : {
41219           name : {
41220             title: "name",
41221             width: 120
41222         },
41223         rows : {
41224             title: "Rows",
41225             width: 20
41226         },
41227         cols : {
41228             title: "Cols",
41229             width: 20
41230         }
41231     },
41232     'SELECT' : {
41233         name : {
41234             title: "name",
41235             width: 120
41236         },
41237         selectoptions : {
41238             title: "Options",
41239             width: 200
41240         }
41241     },
41242     
41243     // should we really allow this??
41244     // should this just be 
41245     'BODY' : {
41246         title : {
41247             title: "title",
41248             width: 200,
41249             disabled : true
41250         }
41251     },
41252     '*' : {
41253         // empty..
41254     }
41255 };
41256
41257
41258
41259 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
41260     
41261     tb: false,
41262     
41263     rendered: false,
41264     
41265     editor : false,
41266     /**
41267      * @cfg {Object} disable  List of toolbar elements to disable
41268          
41269      */
41270     disable : false,
41271     /**
41272      * @cfg {Object} styles List of styles 
41273      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
41274      *
41275      * These must be defined in the page, so they get rendered correctly..
41276      * .headline { }
41277      * TD.underline { }
41278      * 
41279      */
41280     styles : false,
41281     
41282     
41283     
41284     toolbars : false,
41285     
41286     init : function(editor)
41287     {
41288         this.editor = editor;
41289         
41290         
41291         var fid = editor.frameId;
41292         var etb = this;
41293         function btn(id, toggle, handler){
41294             var xid = fid + '-'+ id ;
41295             return {
41296                 id : xid,
41297                 cmd : id,
41298                 cls : 'x-btn-icon x-edit-'+id,
41299                 enableToggle:toggle !== false,
41300                 scope: editor, // was editor...
41301                 handler:handler||editor.relayBtnCmd,
41302                 clickEvent:'mousedown',
41303                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41304                 tabIndex:-1
41305             };
41306         }
41307         // create a new element.
41308         var wdiv = editor.wrap.createChild({
41309                 tag: 'div'
41310             }, editor.wrap.dom.firstChild.nextSibling, true);
41311         
41312         // can we do this more than once??
41313         
41314          // stop form submits
41315       
41316  
41317         // disable everything...
41318         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41319         this.toolbars = {};
41320            
41321         for (var i in  ty) {
41322           
41323             this.toolbars[i] = this.buildToolbar(ty[i],i);
41324         }
41325         this.tb = this.toolbars.BODY;
41326         this.tb.el.show();
41327         this.buildFooter();
41328         this.footer.show();
41329         editor.on('hide', function( ) { this.footer.hide() }, this);
41330         editor.on('show', function( ) { this.footer.show() }, this);
41331         
41332          
41333         this.rendered = true;
41334         
41335         // the all the btns;
41336         editor.on('editorevent', this.updateToolbar, this);
41337         // other toolbars need to implement this..
41338         //editor.on('editmodechange', this.updateToolbar, this);
41339     },
41340     
41341     
41342     
41343     /**
41344      * Protected method that will not generally be called directly. It triggers
41345      * a toolbar update by reading the markup state of the current selection in the editor.
41346      */
41347     updateToolbar: function(editor,ev,sel){
41348
41349         //Roo.log(ev);
41350         // capture mouse up - this is handy for selecting images..
41351         // perhaps should go somewhere else...
41352         if(!this.editor.activated){
41353              this.editor.onFirstFocus();
41354             return;
41355         }
41356         
41357         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
41358         // selectNode - might want to handle IE?
41359         if (ev &&
41360             (ev.type == 'mouseup' || ev.type == 'click' ) &&
41361             ev.target && ev.target.tagName == 'IMG') {
41362             // they have click on an image...
41363             // let's see if we can change the selection...
41364             sel = ev.target;
41365          
41366               var nodeRange = sel.ownerDocument.createRange();
41367             try {
41368                 nodeRange.selectNode(sel);
41369             } catch (e) {
41370                 nodeRange.selectNodeContents(sel);
41371             }
41372             //nodeRange.collapse(true);
41373             var s = editor.win.getSelection();
41374             s.removeAllRanges();
41375             s.addRange(nodeRange);
41376         }  
41377         
41378       
41379         var updateFooter = sel ? false : true;
41380         
41381         
41382         var ans = this.editor.getAllAncestors();
41383         
41384         // pick
41385         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
41386         
41387         if (!sel) { 
41388             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
41389             sel = sel ? sel : this.editor.doc.body;
41390             sel = sel.tagName.length ? sel : this.editor.doc.body;
41391             
41392         }
41393         // pick a menu that exists..
41394         var tn = sel.tagName.toUpperCase();
41395         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
41396         
41397         tn = sel.tagName.toUpperCase();
41398         
41399         var lastSel = this.tb.selectedNode
41400         
41401         this.tb.selectedNode = sel;
41402         
41403         // if current menu does not match..
41404         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
41405                 
41406             this.tb.el.hide();
41407             ///console.log("show: " + tn);
41408             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
41409             this.tb.el.show();
41410             // update name
41411             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
41412             
41413             
41414             // update attributes
41415             if (this.tb.fields) {
41416                 this.tb.fields.each(function(e) {
41417                    e.setValue(sel.getAttribute(e.attrname));
41418                 });
41419             }
41420             
41421             var hasStyles = false;
41422             for(var i in this.styles) {
41423                 hasStyles = true;
41424                 break;
41425             }
41426             
41427             // update styles
41428             if (hasStyles) { 
41429                 var st = this.tb.fields.item(0);
41430                 
41431                 st.store.removeAll();
41432                
41433                 
41434                 var cn = sel.className.split(/\s+/);
41435                 
41436                 var avs = [];
41437                 if (this.styles['*']) {
41438                     
41439                     Roo.each(this.styles['*'], function(v) {
41440                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41441                     });
41442                 }
41443                 if (this.styles[tn]) { 
41444                     Roo.each(this.styles[tn], function(v) {
41445                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
41446                     });
41447                 }
41448                 
41449                 st.store.loadData(avs);
41450                 st.collapse();
41451                 st.setValue(cn);
41452             }
41453             // flag our selected Node.
41454             this.tb.selectedNode = sel;
41455            
41456            
41457             Roo.menu.MenuMgr.hideAll();
41458
41459         }
41460         
41461         if (!updateFooter) {
41462             return;
41463         }
41464         // update the footer
41465         //
41466         var html = '';
41467         
41468         this.footerEls = ans.reverse();
41469         Roo.each(this.footerEls, function(a,i) {
41470             if (!a) { return; }
41471             html += html.length ? ' &gt; '  :  '';
41472             
41473             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
41474             
41475         });
41476        
41477         // 
41478         var sz = this.footDisp.up('td').getSize();
41479         this.footDisp.dom.style.width = (sz.width -10) + 'px';
41480         this.footDisp.dom.style.marginLeft = '5px';
41481         
41482         this.footDisp.dom.style.overflow = 'hidden';
41483         
41484         this.footDisp.dom.innerHTML = html;
41485             
41486         //this.editorsyncValue();
41487     },
41488    
41489        
41490     // private
41491     onDestroy : function(){
41492         if(this.rendered){
41493             
41494             this.tb.items.each(function(item){
41495                 if(item.menu){
41496                     item.menu.removeAll();
41497                     if(item.menu.el){
41498                         item.menu.el.destroy();
41499                     }
41500                 }
41501                 item.destroy();
41502             });
41503              
41504         }
41505     },
41506     onFirstFocus: function() {
41507         // need to do this for all the toolbars..
41508         this.tb.items.each(function(item){
41509            item.enable();
41510         });
41511     },
41512     buildToolbar: function(tlist, nm)
41513     {
41514         var editor = this.editor;
41515          // create a new element.
41516         var wdiv = editor.wrap.createChild({
41517                 tag: 'div'
41518             }, editor.wrap.dom.firstChild.nextSibling, true);
41519         
41520        
41521         var tb = new Roo.Toolbar(wdiv);
41522         // add the name..
41523         
41524         tb.add(nm+ ":&nbsp;");
41525         
41526         var styles = [];
41527         for(var i in this.styles) {
41528             styles.push(i);
41529         }
41530         
41531         // styles...
41532         if (styles && styles.length) {
41533             
41534             // this needs a multi-select checkbox...
41535             tb.addField( new Roo.form.ComboBox({
41536                 store: new Roo.data.SimpleStore({
41537                     id : 'val',
41538                     fields: ['val', 'selected'],
41539                     data : [] 
41540                 }),
41541                 name : '-roo-edit-className',
41542                 attrname : 'className',
41543                 displayField:'val',
41544                 typeAhead: false,
41545                 mode: 'local',
41546                 editable : false,
41547                 triggerAction: 'all',
41548                 emptyText:'Select Style',
41549                 selectOnFocus:true,
41550                 width: 130,
41551                 listeners : {
41552                     'select': function(c, r, i) {
41553                         // initial support only for on class per el..
41554                         tb.selectedNode.className =  r ? r.get('val') : '';
41555                         editor.syncValue();
41556                     }
41557                 }
41558     
41559             }));
41560         }
41561             
41562         
41563         
41564         for (var i in tlist) {
41565             
41566             var item = tlist[i];
41567             tb.add(item.title + ":&nbsp;");
41568             
41569             
41570             
41571             
41572             if (item.opts) {
41573                 // opts == pulldown..
41574                 tb.addField( new Roo.form.ComboBox({
41575                     store: new Roo.data.SimpleStore({
41576                         id : 'val',
41577                         fields: ['val'],
41578                         data : item.opts  
41579                     }),
41580                     name : '-roo-edit-' + i,
41581                     attrname : i,
41582                     displayField:'val',
41583                     typeAhead: false,
41584                     mode: 'local',
41585                     editable : false,
41586                     triggerAction: 'all',
41587                     emptyText:'Select',
41588                     selectOnFocus:true,
41589                     width: item.width ? item.width  : 130,
41590                     listeners : {
41591                         'select': function(c, r, i) {
41592                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
41593                         }
41594                     }
41595
41596                 }));
41597                 continue;
41598                     
41599                  
41600                 
41601                 tb.addField( new Roo.form.TextField({
41602                     name: i,
41603                     width: 100,
41604                     //allowBlank:false,
41605                     value: ''
41606                 }));
41607                 continue;
41608             }
41609             tb.addField( new Roo.form.TextField({
41610                 name: '-roo-edit-' + i,
41611                 attrname : i,
41612                 
41613                 width: item.width,
41614                 //allowBlank:true,
41615                 value: '',
41616                 listeners: {
41617                     'change' : function(f, nv, ov) {
41618                         tb.selectedNode.setAttribute(f.attrname, nv);
41619                     }
41620                 }
41621             }));
41622              
41623         }
41624         tb.el.on('click', function(e){
41625             e.preventDefault(); // what does this do?
41626         });
41627         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41628         tb.el.hide();
41629         tb.name = nm;
41630         // dont need to disable them... as they will get hidden
41631         return tb;
41632          
41633         
41634     },
41635     buildFooter : function()
41636     {
41637         
41638         var fel = this.editor.wrap.createChild();
41639         this.footer = new Roo.Toolbar(fel);
41640         // toolbar has scrolly on left / right?
41641         var footDisp= new Roo.Toolbar.Fill();
41642         var _t = this;
41643         this.footer.add(
41644             {
41645                 text : '&lt;',
41646                 xtype: 'Button',
41647                 handler : function() {
41648                     _t.footDisp.scrollTo('left',0,true)
41649                 }
41650             }
41651         );
41652         this.footer.add( footDisp );
41653         this.footer.add( 
41654             {
41655                 text : '&gt;',
41656                 xtype: 'Button',
41657                 handler : function() {
41658                     // no animation..
41659                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41660                 }
41661             }
41662         );
41663         var fel = Roo.get(footDisp.el);
41664         fel.addClass('x-editor-context');
41665         this.footDispWrap = fel; 
41666         this.footDispWrap.overflow  = 'hidden';
41667         
41668         this.footDisp = fel.createChild();
41669         this.footDispWrap.on('click', this.onContextClick, this)
41670         
41671         
41672     },
41673     onContextClick : function (ev,dom)
41674     {
41675         ev.preventDefault();
41676         var  cn = dom.className;
41677         Roo.log(cn);
41678         if (!cn.match(/x-ed-loc-/)) {
41679             return;
41680         }
41681         var n = cn.split('-').pop();
41682         var ans = this.footerEls;
41683         var sel = ans[n];
41684         
41685          // pick
41686         var range = this.editor.createRange();
41687         
41688         range.selectNodeContents(sel);
41689         //range.selectNode(sel);
41690         
41691         
41692         var selection = this.editor.getSelection();
41693         selection.removeAllRanges();
41694         selection.addRange(range);
41695         
41696         
41697         
41698         this.updateToolbar(null, null, sel);
41699         
41700         
41701     }
41702     
41703     
41704     
41705     
41706     
41707 });
41708
41709
41710
41711
41712
41713 /*
41714  * Based on:
41715  * Ext JS Library 1.1.1
41716  * Copyright(c) 2006-2007, Ext JS, LLC.
41717  *
41718  * Originally Released Under LGPL - original licence link has changed is not relivant.
41719  *
41720  * Fork - LGPL
41721  * <script type="text/javascript">
41722  */
41723  
41724 /**
41725  * @class Roo.form.BasicForm
41726  * @extends Roo.util.Observable
41727  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41728  * @constructor
41729  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41730  * @param {Object} config Configuration options
41731  */
41732 Roo.form.BasicForm = function(el, config){
41733     this.allItems = [];
41734     this.childForms = [];
41735     Roo.apply(this, config);
41736     /*
41737      * The Roo.form.Field items in this form.
41738      * @type MixedCollection
41739      */
41740      
41741      
41742     this.items = new Roo.util.MixedCollection(false, function(o){
41743         return o.id || (o.id = Roo.id());
41744     });
41745     this.addEvents({
41746         /**
41747          * @event beforeaction
41748          * Fires before any action is performed. Return false to cancel the action.
41749          * @param {Form} this
41750          * @param {Action} action The action to be performed
41751          */
41752         beforeaction: true,
41753         /**
41754          * @event actionfailed
41755          * Fires when an action fails.
41756          * @param {Form} this
41757          * @param {Action} action The action that failed
41758          */
41759         actionfailed : true,
41760         /**
41761          * @event actioncomplete
41762          * Fires when an action is completed.
41763          * @param {Form} this
41764          * @param {Action} action The action that completed
41765          */
41766         actioncomplete : true
41767     });
41768     if(el){
41769         this.initEl(el);
41770     }
41771     Roo.form.BasicForm.superclass.constructor.call(this);
41772 };
41773
41774 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41775     /**
41776      * @cfg {String} method
41777      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41778      */
41779     /**
41780      * @cfg {DataReader} reader
41781      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41782      * This is optional as there is built-in support for processing JSON.
41783      */
41784     /**
41785      * @cfg {DataReader} errorReader
41786      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41787      * This is completely optional as there is built-in support for processing JSON.
41788      */
41789     /**
41790      * @cfg {String} url
41791      * The URL to use for form actions if one isn't supplied in the action options.
41792      */
41793     /**
41794      * @cfg {Boolean} fileUpload
41795      * Set to true if this form is a file upload.
41796      */
41797      
41798     /**
41799      * @cfg {Object} baseParams
41800      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41801      */
41802      /**
41803      
41804     /**
41805      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41806      */
41807     timeout: 30,
41808
41809     // private
41810     activeAction : null,
41811
41812     /**
41813      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41814      * or setValues() data instead of when the form was first created.
41815      */
41816     trackResetOnLoad : false,
41817     
41818     
41819     /**
41820      * childForms - used for multi-tab forms
41821      * @type {Array}
41822      */
41823     childForms : false,
41824     
41825     /**
41826      * allItems - full list of fields.
41827      * @type {Array}
41828      */
41829     allItems : false,
41830     
41831     /**
41832      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41833      * element by passing it or its id or mask the form itself by passing in true.
41834      * @type Mixed
41835      */
41836     waitMsgTarget : false,
41837
41838     // private
41839     initEl : function(el){
41840         this.el = Roo.get(el);
41841         this.id = this.el.id || Roo.id();
41842         this.el.on('submit', this.onSubmit, this);
41843         this.el.addClass('x-form');
41844     },
41845
41846     // private
41847     onSubmit : function(e){
41848         e.stopEvent();
41849     },
41850
41851     /**
41852      * Returns true if client-side validation on the form is successful.
41853      * @return Boolean
41854      */
41855     isValid : function(){
41856         var valid = true;
41857         this.items.each(function(f){
41858            if(!f.validate()){
41859                valid = false;
41860            }
41861         });
41862         return valid;
41863     },
41864
41865     /**
41866      * Returns true if any fields in this form have changed since their original load.
41867      * @return Boolean
41868      */
41869     isDirty : function(){
41870         var dirty = false;
41871         this.items.each(function(f){
41872            if(f.isDirty()){
41873                dirty = true;
41874                return false;
41875            }
41876         });
41877         return dirty;
41878     },
41879
41880     /**
41881      * Performs a predefined action (submit or load) or custom actions you define on this form.
41882      * @param {String} actionName The name of the action type
41883      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41884      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41885      * accept other config options):
41886      * <pre>
41887 Property          Type             Description
41888 ----------------  ---------------  ----------------------------------------------------------------------------------
41889 url               String           The url for the action (defaults to the form's url)
41890 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41891 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41892 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41893                                    validate the form on the client (defaults to false)
41894      * </pre>
41895      * @return {BasicForm} this
41896      */
41897     doAction : function(action, options){
41898         if(typeof action == 'string'){
41899             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41900         }
41901         if(this.fireEvent('beforeaction', this, action) !== false){
41902             this.beforeAction(action);
41903             action.run.defer(100, action);
41904         }
41905         return this;
41906     },
41907
41908     /**
41909      * Shortcut to do a submit action.
41910      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41911      * @return {BasicForm} this
41912      */
41913     submit : function(options){
41914         this.doAction('submit', options);
41915         return this;
41916     },
41917
41918     /**
41919      * Shortcut to do a load action.
41920      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41921      * @return {BasicForm} this
41922      */
41923     load : function(options){
41924         this.doAction('load', options);
41925         return this;
41926     },
41927
41928     /**
41929      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41930      * @param {Record} record The record to edit
41931      * @return {BasicForm} this
41932      */
41933     updateRecord : function(record){
41934         record.beginEdit();
41935         var fs = record.fields;
41936         fs.each(function(f){
41937             var field = this.findField(f.name);
41938             if(field){
41939                 record.set(f.name, field.getValue());
41940             }
41941         }, this);
41942         record.endEdit();
41943         return this;
41944     },
41945
41946     /**
41947      * Loads an Roo.data.Record into this form.
41948      * @param {Record} record The record to load
41949      * @return {BasicForm} this
41950      */
41951     loadRecord : function(record){
41952         this.setValues(record.data);
41953         return this;
41954     },
41955
41956     // private
41957     beforeAction : function(action){
41958         var o = action.options;
41959         
41960        
41961         if(this.waitMsgTarget === true){
41962             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41963         }else if(this.waitMsgTarget){
41964             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41965             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41966         }else {
41967             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41968         }
41969          
41970     },
41971
41972     // private
41973     afterAction : function(action, success){
41974         this.activeAction = null;
41975         var o = action.options;
41976         
41977         if(this.waitMsgTarget === true){
41978             this.el.unmask();
41979         }else if(this.waitMsgTarget){
41980             this.waitMsgTarget.unmask();
41981         }else{
41982             Roo.MessageBox.updateProgress(1);
41983             Roo.MessageBox.hide();
41984         }
41985          
41986         if(success){
41987             if(o.reset){
41988                 this.reset();
41989             }
41990             Roo.callback(o.success, o.scope, [this, action]);
41991             this.fireEvent('actioncomplete', this, action);
41992             
41993         }else{
41994             
41995             // failure condition..
41996             // we have a scenario where updates need confirming.
41997             // eg. if a locking scenario exists..
41998             // we look for { errors : { needs_confirm : true }} in the response.
41999             if (
42000                 (typeof(action.result) != 'undefined')  &&
42001                 (typeof(action.result.errors) != 'undefined')  &&
42002                 (typeof(action.result.errors.needs_confirm) != 'undefined')
42003           ){
42004                 var _t = this;
42005                 Roo.MessageBox.confirm(
42006                     "Change requires confirmation",
42007                     action.result.errorMsg,
42008                     function(r) {
42009                         if (r != 'yes') {
42010                             return;
42011                         }
42012                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
42013                     }
42014                     
42015                 );
42016                 
42017                 
42018                 
42019                 return;
42020             }
42021             
42022             Roo.callback(o.failure, o.scope, [this, action]);
42023             // show an error message if no failed handler is set..
42024             if (!this.hasListener('actionfailed')) {
42025                 Roo.MessageBox.alert("Error",
42026                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
42027                         action.result.errorMsg :
42028                         "Saving Failed, please check your entries or try again"
42029                 );
42030             }
42031             
42032             this.fireEvent('actionfailed', this, action);
42033         }
42034         
42035     },
42036
42037     /**
42038      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
42039      * @param {String} id The value to search for
42040      * @return Field
42041      */
42042     findField : function(id){
42043         var field = this.items.get(id);
42044         if(!field){
42045             this.items.each(function(f){
42046                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
42047                     field = f;
42048                     return false;
42049                 }
42050             });
42051         }
42052         return field || null;
42053     },
42054
42055     /**
42056      * Add a secondary form to this one, 
42057      * Used to provide tabbed forms. One form is primary, with hidden values 
42058      * which mirror the elements from the other forms.
42059      * 
42060      * @param {Roo.form.Form} form to add.
42061      * 
42062      */
42063     addForm : function(form)
42064     {
42065        
42066         if (this.childForms.indexOf(form) > -1) {
42067             // already added..
42068             return;
42069         }
42070         this.childForms.push(form);
42071         var n = '';
42072         Roo.each(form.allItems, function (fe) {
42073             
42074             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
42075             if (this.findField(n)) { // already added..
42076                 return;
42077             }
42078             var add = new Roo.form.Hidden({
42079                 name : n
42080             });
42081             add.render(this.el);
42082             
42083             this.add( add );
42084         }, this);
42085         
42086     },
42087     /**
42088      * Mark fields in this form invalid in bulk.
42089      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
42090      * @return {BasicForm} this
42091      */
42092     markInvalid : function(errors){
42093         if(errors instanceof Array){
42094             for(var i = 0, len = errors.length; i < len; i++){
42095                 var fieldError = errors[i];
42096                 var f = this.findField(fieldError.id);
42097                 if(f){
42098                     f.markInvalid(fieldError.msg);
42099                 }
42100             }
42101         }else{
42102             var field, id;
42103             for(id in errors){
42104                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
42105                     field.markInvalid(errors[id]);
42106                 }
42107             }
42108         }
42109         Roo.each(this.childForms || [], function (f) {
42110             f.markInvalid(errors);
42111         });
42112         
42113         return this;
42114     },
42115
42116     /**
42117      * Set values for fields in this form in bulk.
42118      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
42119      * @return {BasicForm} this
42120      */
42121     setValues : function(values){
42122         if(values instanceof Array){ // array of objects
42123             for(var i = 0, len = values.length; i < len; i++){
42124                 var v = values[i];
42125                 var f = this.findField(v.id);
42126                 if(f){
42127                     f.setValue(v.value);
42128                     if(this.trackResetOnLoad){
42129                         f.originalValue = f.getValue();
42130                     }
42131                 }
42132             }
42133         }else{ // object hash
42134             var field, id;
42135             for(id in values){
42136                 if(typeof values[id] != 'function' && (field = this.findField(id))){
42137                     
42138                     if (field.setFromData && 
42139                         field.valueField && 
42140                         field.displayField &&
42141                         // combos' with local stores can 
42142                         // be queried via setValue()
42143                         // to set their value..
42144                         (field.store && !field.store.isLocal)
42145                         ) {
42146                         // it's a combo
42147                         var sd = { };
42148                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
42149                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
42150                         field.setFromData(sd);
42151                         
42152                     } else {
42153                         field.setValue(values[id]);
42154                     }
42155                     
42156                     
42157                     if(this.trackResetOnLoad){
42158                         field.originalValue = field.getValue();
42159                     }
42160                 }
42161             }
42162         }
42163          
42164         Roo.each(this.childForms || [], function (f) {
42165             f.setValues(values);
42166         });
42167                 
42168         return this;
42169     },
42170
42171     /**
42172      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
42173      * they are returned as an array.
42174      * @param {Boolean} asString
42175      * @return {Object}
42176      */
42177     getValues : function(asString){
42178         if (this.childForms) {
42179             // copy values from the child forms
42180             Roo.each(this.childForms, function (f) {
42181                 this.setValues(f.getValues());
42182             }, this);
42183         }
42184         
42185         
42186         
42187         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
42188         if(asString === true){
42189             return fs;
42190         }
42191         return Roo.urlDecode(fs);
42192     },
42193     
42194     /**
42195      * Returns the fields in this form as an object with key/value pairs. 
42196      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
42197      * @return {Object}
42198      */
42199     getFieldValues : function(with_hidden)
42200     {
42201         if (this.childForms) {
42202             // copy values from the child forms
42203             // should this call getFieldValues - probably not as we do not currently copy
42204             // hidden fields when we generate..
42205             Roo.each(this.childForms, function (f) {
42206                 this.setValues(f.getValues());
42207             }, this);
42208         }
42209         
42210         var ret = {};
42211         this.items.each(function(f){
42212             if (!f.getName()) {
42213                 return;
42214             }
42215             var v = f.getValue();
42216             // not sure if this supported any more..
42217             if ((typeof(v) == 'object') && f.getRawValue) {
42218                 v = f.getRawValue() ; // dates..
42219             }
42220             // combo boxes where name != hiddenName...
42221             if (f.name != f.getName()) {
42222                 ret[f.name] = f.getRawValue();
42223             }
42224             ret[f.getName()] = v;
42225         });
42226         
42227         return ret;
42228     },
42229
42230     /**
42231      * Clears all invalid messages in this form.
42232      * @return {BasicForm} this
42233      */
42234     clearInvalid : function(){
42235         this.items.each(function(f){
42236            f.clearInvalid();
42237         });
42238         
42239         Roo.each(this.childForms || [], function (f) {
42240             f.clearInvalid();
42241         });
42242         
42243         
42244         return this;
42245     },
42246
42247     /**
42248      * Resets this form.
42249      * @return {BasicForm} this
42250      */
42251     reset : function(){
42252         this.items.each(function(f){
42253             f.reset();
42254         });
42255         
42256         Roo.each(this.childForms || [], function (f) {
42257             f.reset();
42258         });
42259        
42260         
42261         return this;
42262     },
42263
42264     /**
42265      * Add Roo.form components to this form.
42266      * @param {Field} field1
42267      * @param {Field} field2 (optional)
42268      * @param {Field} etc (optional)
42269      * @return {BasicForm} this
42270      */
42271     add : function(){
42272         this.items.addAll(Array.prototype.slice.call(arguments, 0));
42273         return this;
42274     },
42275
42276
42277     /**
42278      * Removes a field from the items collection (does NOT remove its markup).
42279      * @param {Field} field
42280      * @return {BasicForm} this
42281      */
42282     remove : function(field){
42283         this.items.remove(field);
42284         return this;
42285     },
42286
42287     /**
42288      * Looks at the fields in this form, checks them for an id attribute,
42289      * and calls applyTo on the existing dom element with that id.
42290      * @return {BasicForm} this
42291      */
42292     render : function(){
42293         this.items.each(function(f){
42294             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
42295                 f.applyTo(f.id);
42296             }
42297         });
42298         return this;
42299     },
42300
42301     /**
42302      * Calls {@link Ext#apply} for all fields in this form with the passed object.
42303      * @param {Object} values
42304      * @return {BasicForm} this
42305      */
42306     applyToFields : function(o){
42307         this.items.each(function(f){
42308            Roo.apply(f, o);
42309         });
42310         return this;
42311     },
42312
42313     /**
42314      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
42315      * @param {Object} values
42316      * @return {BasicForm} this
42317      */
42318     applyIfToFields : function(o){
42319         this.items.each(function(f){
42320            Roo.applyIf(f, o);
42321         });
42322         return this;
42323     }
42324 });
42325
42326 // back compat
42327 Roo.BasicForm = Roo.form.BasicForm;/*
42328  * Based on:
42329  * Ext JS Library 1.1.1
42330  * Copyright(c) 2006-2007, Ext JS, LLC.
42331  *
42332  * Originally Released Under LGPL - original licence link has changed is not relivant.
42333  *
42334  * Fork - LGPL
42335  * <script type="text/javascript">
42336  */
42337
42338 /**
42339  * @class Roo.form.Form
42340  * @extends Roo.form.BasicForm
42341  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
42342  * @constructor
42343  * @param {Object} config Configuration options
42344  */
42345 Roo.form.Form = function(config){
42346     var xitems =  [];
42347     if (config.items) {
42348         xitems = config.items;
42349         delete config.items;
42350     }
42351    
42352     
42353     Roo.form.Form.superclass.constructor.call(this, null, config);
42354     this.url = this.url || this.action;
42355     if(!this.root){
42356         this.root = new Roo.form.Layout(Roo.applyIf({
42357             id: Roo.id()
42358         }, config));
42359     }
42360     this.active = this.root;
42361     /**
42362      * Array of all the buttons that have been added to this form via {@link addButton}
42363      * @type Array
42364      */
42365     this.buttons = [];
42366     this.allItems = [];
42367     this.addEvents({
42368         /**
42369          * @event clientvalidation
42370          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
42371          * @param {Form} this
42372          * @param {Boolean} valid true if the form has passed client-side validation
42373          */
42374         clientvalidation: true,
42375         /**
42376          * @event rendered
42377          * Fires when the form is rendered
42378          * @param {Roo.form.Form} form
42379          */
42380         rendered : true
42381     });
42382     
42383     if (this.progressUrl) {
42384             // push a hidden field onto the list of fields..
42385             this.addxtype( {
42386                     xns: Roo.form, 
42387                     xtype : 'Hidden', 
42388                     name : 'UPLOAD_IDENTIFIER' 
42389             });
42390         }
42391         
42392     
42393     Roo.each(xitems, this.addxtype, this);
42394     
42395     
42396     
42397 };
42398
42399 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
42400     /**
42401      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
42402      */
42403     /**
42404      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
42405      */
42406     /**
42407      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
42408      */
42409     buttonAlign:'center',
42410
42411     /**
42412      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
42413      */
42414     minButtonWidth:75,
42415
42416     /**
42417      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
42418      * This property cascades to child containers if not set.
42419      */
42420     labelAlign:'left',
42421
42422     /**
42423      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
42424      * fires a looping event with that state. This is required to bind buttons to the valid
42425      * state using the config value formBind:true on the button.
42426      */
42427     monitorValid : false,
42428
42429     /**
42430      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
42431      */
42432     monitorPoll : 200,
42433     
42434     /**
42435      * @cfg {String} progressUrl - Url to return progress data 
42436      */
42437     
42438     progressUrl : false,
42439   
42440     /**
42441      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
42442      * fields are added and the column is closed. If no fields are passed the column remains open
42443      * until end() is called.
42444      * @param {Object} config The config to pass to the column
42445      * @param {Field} field1 (optional)
42446      * @param {Field} field2 (optional)
42447      * @param {Field} etc (optional)
42448      * @return Column The column container object
42449      */
42450     column : function(c){
42451         var col = new Roo.form.Column(c);
42452         this.start(col);
42453         if(arguments.length > 1){ // duplicate code required because of Opera
42454             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42455             this.end();
42456         }
42457         return col;
42458     },
42459
42460     /**
42461      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
42462      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
42463      * until end() is called.
42464      * @param {Object} config The config to pass to the fieldset
42465      * @param {Field} field1 (optional)
42466      * @param {Field} field2 (optional)
42467      * @param {Field} etc (optional)
42468      * @return FieldSet The fieldset container object
42469      */
42470     fieldset : function(c){
42471         var fs = new Roo.form.FieldSet(c);
42472         this.start(fs);
42473         if(arguments.length > 1){ // duplicate code required because of Opera
42474             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42475             this.end();
42476         }
42477         return fs;
42478     },
42479
42480     /**
42481      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
42482      * fields are added and the container is closed. If no fields are passed the container remains open
42483      * until end() is called.
42484      * @param {Object} config The config to pass to the Layout
42485      * @param {Field} field1 (optional)
42486      * @param {Field} field2 (optional)
42487      * @param {Field} etc (optional)
42488      * @return Layout The container object
42489      */
42490     container : function(c){
42491         var l = new Roo.form.Layout(c);
42492         this.start(l);
42493         if(arguments.length > 1){ // duplicate code required because of Opera
42494             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
42495             this.end();
42496         }
42497         return l;
42498     },
42499
42500     /**
42501      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
42502      * @param {Object} container A Roo.form.Layout or subclass of Layout
42503      * @return {Form} this
42504      */
42505     start : function(c){
42506         // cascade label info
42507         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
42508         this.active.stack.push(c);
42509         c.ownerCt = this.active;
42510         this.active = c;
42511         return this;
42512     },
42513
42514     /**
42515      * Closes the current open container
42516      * @return {Form} this
42517      */
42518     end : function(){
42519         if(this.active == this.root){
42520             return this;
42521         }
42522         this.active = this.active.ownerCt;
42523         return this;
42524     },
42525
42526     /**
42527      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
42528      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
42529      * as the label of the field.
42530      * @param {Field} field1
42531      * @param {Field} field2 (optional)
42532      * @param {Field} etc. (optional)
42533      * @return {Form} this
42534      */
42535     add : function(){
42536         this.active.stack.push.apply(this.active.stack, arguments);
42537         this.allItems.push.apply(this.allItems,arguments);
42538         var r = [];
42539         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
42540             if(a[i].isFormField){
42541                 r.push(a[i]);
42542             }
42543         }
42544         if(r.length > 0){
42545             Roo.form.Form.superclass.add.apply(this, r);
42546         }
42547         return this;
42548     },
42549     
42550
42551     
42552     
42553     
42554      /**
42555      * Find any element that has been added to a form, using it's ID or name
42556      * This can include framesets, columns etc. along with regular fields..
42557      * @param {String} id - id or name to find.
42558      
42559      * @return {Element} e - or false if nothing found.
42560      */
42561     findbyId : function(id)
42562     {
42563         var ret = false;
42564         if (!id) {
42565             return ret;
42566         }
42567         Roo.each(this.allItems, function(f){
42568             if (f.id == id || f.name == id ){
42569                 ret = f;
42570                 return false;
42571             }
42572         });
42573         return ret;
42574     },
42575
42576     
42577     
42578     /**
42579      * Render this form into the passed container. This should only be called once!
42580      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42581      * @return {Form} this
42582      */
42583     render : function(ct)
42584     {
42585         
42586         
42587         
42588         ct = Roo.get(ct);
42589         var o = this.autoCreate || {
42590             tag: 'form',
42591             method : this.method || 'POST',
42592             id : this.id || Roo.id()
42593         };
42594         this.initEl(ct.createChild(o));
42595
42596         this.root.render(this.el);
42597         
42598        
42599              
42600         this.items.each(function(f){
42601             f.render('x-form-el-'+f.id);
42602         });
42603
42604         if(this.buttons.length > 0){
42605             // tables are required to maintain order and for correct IE layout
42606             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42607                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42608                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42609             }}, null, true);
42610             var tr = tb.getElementsByTagName('tr')[0];
42611             for(var i = 0, len = this.buttons.length; i < len; i++) {
42612                 var b = this.buttons[i];
42613                 var td = document.createElement('td');
42614                 td.className = 'x-form-btn-td';
42615                 b.render(tr.appendChild(td));
42616             }
42617         }
42618         if(this.monitorValid){ // initialize after render
42619             this.startMonitoring();
42620         }
42621         this.fireEvent('rendered', this);
42622         return this;
42623     },
42624
42625     /**
42626      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42627      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42628      * object or a valid Roo.DomHelper element config
42629      * @param {Function} handler The function called when the button is clicked
42630      * @param {Object} scope (optional) The scope of the handler function
42631      * @return {Roo.Button}
42632      */
42633     addButton : function(config, handler, scope){
42634         var bc = {
42635             handler: handler,
42636             scope: scope,
42637             minWidth: this.minButtonWidth,
42638             hideParent:true
42639         };
42640         if(typeof config == "string"){
42641             bc.text = config;
42642         }else{
42643             Roo.apply(bc, config);
42644         }
42645         var btn = new Roo.Button(null, bc);
42646         this.buttons.push(btn);
42647         return btn;
42648     },
42649
42650      /**
42651      * Adds a series of form elements (using the xtype property as the factory method.
42652      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42653      * @param {Object} config 
42654      */
42655     
42656     addxtype : function()
42657     {
42658         var ar = Array.prototype.slice.call(arguments, 0);
42659         var ret = false;
42660         for(var i = 0; i < ar.length; i++) {
42661             if (!ar[i]) {
42662                 continue; // skip -- if this happends something invalid got sent, we 
42663                 // should ignore it, as basically that interface element will not show up
42664                 // and that should be pretty obvious!!
42665             }
42666             
42667             if (Roo.form[ar[i].xtype]) {
42668                 ar[i].form = this;
42669                 var fe = Roo.factory(ar[i], Roo.form);
42670                 if (!ret) {
42671                     ret = fe;
42672                 }
42673                 fe.form = this;
42674                 if (fe.store) {
42675                     fe.store.form = this;
42676                 }
42677                 if (fe.isLayout) {  
42678                          
42679                     this.start(fe);
42680                     this.allItems.push(fe);
42681                     if (fe.items && fe.addxtype) {
42682                         fe.addxtype.apply(fe, fe.items);
42683                         delete fe.items;
42684                     }
42685                      this.end();
42686                     continue;
42687                 }
42688                 
42689                 
42690                  
42691                 this.add(fe);
42692               //  console.log('adding ' + ar[i].xtype);
42693             }
42694             if (ar[i].xtype == 'Button') {  
42695                 //console.log('adding button');
42696                 //console.log(ar[i]);
42697                 this.addButton(ar[i]);
42698                 this.allItems.push(fe);
42699                 continue;
42700             }
42701             
42702             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42703                 alert('end is not supported on xtype any more, use items');
42704             //    this.end();
42705             //    //console.log('adding end');
42706             }
42707             
42708         }
42709         return ret;
42710     },
42711     
42712     /**
42713      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42714      * option "monitorValid"
42715      */
42716     startMonitoring : function(){
42717         if(!this.bound){
42718             this.bound = true;
42719             Roo.TaskMgr.start({
42720                 run : this.bindHandler,
42721                 interval : this.monitorPoll || 200,
42722                 scope: this
42723             });
42724         }
42725     },
42726
42727     /**
42728      * Stops monitoring of the valid state of this form
42729      */
42730     stopMonitoring : function(){
42731         this.bound = false;
42732     },
42733
42734     // private
42735     bindHandler : function(){
42736         if(!this.bound){
42737             return false; // stops binding
42738         }
42739         var valid = true;
42740         this.items.each(function(f){
42741             if(!f.isValid(true)){
42742                 valid = false;
42743                 return false;
42744             }
42745         });
42746         for(var i = 0, len = this.buttons.length; i < len; i++){
42747             var btn = this.buttons[i];
42748             if(btn.formBind === true && btn.disabled === valid){
42749                 btn.setDisabled(!valid);
42750             }
42751         }
42752         this.fireEvent('clientvalidation', this, valid);
42753     }
42754     
42755     
42756     
42757     
42758     
42759     
42760     
42761     
42762 });
42763
42764
42765 // back compat
42766 Roo.Form = Roo.form.Form;
42767 /*
42768  * Based on:
42769  * Ext JS Library 1.1.1
42770  * Copyright(c) 2006-2007, Ext JS, LLC.
42771  *
42772  * Originally Released Under LGPL - original licence link has changed is not relivant.
42773  *
42774  * Fork - LGPL
42775  * <script type="text/javascript">
42776  */
42777  
42778  /**
42779  * @class Roo.form.Action
42780  * Internal Class used to handle form actions
42781  * @constructor
42782  * @param {Roo.form.BasicForm} el The form element or its id
42783  * @param {Object} config Configuration options
42784  */
42785  
42786  
42787 // define the action interface
42788 Roo.form.Action = function(form, options){
42789     this.form = form;
42790     this.options = options || {};
42791 };
42792 /**
42793  * Client Validation Failed
42794  * @const 
42795  */
42796 Roo.form.Action.CLIENT_INVALID = 'client';
42797 /**
42798  * Server Validation Failed
42799  * @const 
42800  */
42801  Roo.form.Action.SERVER_INVALID = 'server';
42802  /**
42803  * Connect to Server Failed
42804  * @const 
42805  */
42806 Roo.form.Action.CONNECT_FAILURE = 'connect';
42807 /**
42808  * Reading Data from Server Failed
42809  * @const 
42810  */
42811 Roo.form.Action.LOAD_FAILURE = 'load';
42812
42813 Roo.form.Action.prototype = {
42814     type : 'default',
42815     failureType : undefined,
42816     response : undefined,
42817     result : undefined,
42818
42819     // interface method
42820     run : function(options){
42821
42822     },
42823
42824     // interface method
42825     success : function(response){
42826
42827     },
42828
42829     // interface method
42830     handleResponse : function(response){
42831
42832     },
42833
42834     // default connection failure
42835     failure : function(response){
42836         
42837         this.response = response;
42838         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42839         this.form.afterAction(this, false);
42840     },
42841
42842     processResponse : function(response){
42843         this.response = response;
42844         if(!response.responseText){
42845             return true;
42846         }
42847         this.result = this.handleResponse(response);
42848         return this.result;
42849     },
42850
42851     // utility functions used internally
42852     getUrl : function(appendParams){
42853         var url = this.options.url || this.form.url || this.form.el.dom.action;
42854         if(appendParams){
42855             var p = this.getParams();
42856             if(p){
42857                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42858             }
42859         }
42860         return url;
42861     },
42862
42863     getMethod : function(){
42864         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42865     },
42866
42867     getParams : function(){
42868         var bp = this.form.baseParams;
42869         var p = this.options.params;
42870         if(p){
42871             if(typeof p == "object"){
42872                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42873             }else if(typeof p == 'string' && bp){
42874                 p += '&' + Roo.urlEncode(bp);
42875             }
42876         }else if(bp){
42877             p = Roo.urlEncode(bp);
42878         }
42879         return p;
42880     },
42881
42882     createCallback : function(){
42883         return {
42884             success: this.success,
42885             failure: this.failure,
42886             scope: this,
42887             timeout: (this.form.timeout*1000),
42888             upload: this.form.fileUpload ? this.success : undefined
42889         };
42890     }
42891 };
42892
42893 Roo.form.Action.Submit = function(form, options){
42894     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42895 };
42896
42897 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42898     type : 'submit',
42899
42900     haveProgress : false,
42901     uploadComplete : false,
42902     
42903     // uploadProgress indicator.
42904     uploadProgress : function()
42905     {
42906         if (!this.form.progressUrl) {
42907             return;
42908         }
42909         
42910         if (!this.haveProgress) {
42911             Roo.MessageBox.progress("Uploading", "Uploading");
42912         }
42913         if (this.uploadComplete) {
42914            Roo.MessageBox.hide();
42915            return;
42916         }
42917         
42918         this.haveProgress = true;
42919    
42920         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42921         
42922         var c = new Roo.data.Connection();
42923         c.request({
42924             url : this.form.progressUrl,
42925             params: {
42926                 id : uid
42927             },
42928             method: 'GET',
42929             success : function(req){
42930                //console.log(data);
42931                 var rdata = false;
42932                 var edata;
42933                 try  {
42934                    rdata = Roo.decode(req.responseText)
42935                 } catch (e) {
42936                     Roo.log("Invalid data from server..");
42937                     Roo.log(edata);
42938                     return;
42939                 }
42940                 if (!rdata || !rdata.success) {
42941                     Roo.log(rdata);
42942                     return;
42943                 }
42944                 var data = rdata.data;
42945                 
42946                 if (this.uploadComplete) {
42947                    Roo.MessageBox.hide();
42948                    return;
42949                 }
42950                    
42951                 if (data){
42952                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42953                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42954                     );
42955                 }
42956                 this.uploadProgress.defer(2000,this);
42957             },
42958        
42959             failure: function(data) {
42960                 Roo.log('progress url failed ');
42961                 Roo.log(data);
42962             },
42963             scope : this
42964         });
42965            
42966     },
42967     
42968     
42969     run : function()
42970     {
42971         // run get Values on the form, so it syncs any secondary forms.
42972         this.form.getValues();
42973         
42974         var o = this.options;
42975         var method = this.getMethod();
42976         var isPost = method == 'POST';
42977         if(o.clientValidation === false || this.form.isValid()){
42978             
42979             if (this.form.progressUrl) {
42980                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42981                     (new Date() * 1) + '' + Math.random());
42982                     
42983             } 
42984             
42985             
42986             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42987                 form:this.form.el.dom,
42988                 url:this.getUrl(!isPost),
42989                 method: method,
42990                 params:isPost ? this.getParams() : null,
42991                 isUpload: this.form.fileUpload
42992             }));
42993             
42994             this.uploadProgress();
42995
42996         }else if (o.clientValidation !== false){ // client validation failed
42997             this.failureType = Roo.form.Action.CLIENT_INVALID;
42998             this.form.afterAction(this, false);
42999         }
43000     },
43001
43002     success : function(response)
43003     {
43004         this.uploadComplete= true;
43005         if (this.haveProgress) {
43006             Roo.MessageBox.hide();
43007         }
43008         
43009         
43010         var result = this.processResponse(response);
43011         if(result === true || result.success){
43012             this.form.afterAction(this, true);
43013             return;
43014         }
43015         if(result.errors){
43016             this.form.markInvalid(result.errors);
43017             this.failureType = Roo.form.Action.SERVER_INVALID;
43018         }
43019         this.form.afterAction(this, false);
43020     },
43021     failure : function(response)
43022     {
43023         this.uploadComplete= true;
43024         if (this.haveProgress) {
43025             Roo.MessageBox.hide();
43026         }
43027         
43028         this.response = response;
43029         this.failureType = Roo.form.Action.CONNECT_FAILURE;
43030         this.form.afterAction(this, false);
43031     },
43032     
43033     handleResponse : function(response){
43034         if(this.form.errorReader){
43035             var rs = this.form.errorReader.read(response);
43036             var errors = [];
43037             if(rs.records){
43038                 for(var i = 0, len = rs.records.length; i < len; i++) {
43039                     var r = rs.records[i];
43040                     errors[i] = r.data;
43041                 }
43042             }
43043             if(errors.length < 1){
43044                 errors = null;
43045             }
43046             return {
43047                 success : rs.success,
43048                 errors : errors
43049             };
43050         }
43051         var ret = false;
43052         try {
43053             ret = Roo.decode(response.responseText);
43054         } catch (e) {
43055             ret = {
43056                 success: false,
43057                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
43058                 errors : []
43059             };
43060         }
43061         return ret;
43062         
43063     }
43064 });
43065
43066
43067 Roo.form.Action.Load = function(form, options){
43068     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
43069     this.reader = this.form.reader;
43070 };
43071
43072 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
43073     type : 'load',
43074
43075     run : function(){
43076         
43077         Roo.Ajax.request(Roo.apply(
43078                 this.createCallback(), {
43079                     method:this.getMethod(),
43080                     url:this.getUrl(false),
43081                     params:this.getParams()
43082         }));
43083     },
43084
43085     success : function(response){
43086         
43087         var result = this.processResponse(response);
43088         if(result === true || !result.success || !result.data){
43089             this.failureType = Roo.form.Action.LOAD_FAILURE;
43090             this.form.afterAction(this, false);
43091             return;
43092         }
43093         this.form.clearInvalid();
43094         this.form.setValues(result.data);
43095         this.form.afterAction(this, true);
43096     },
43097
43098     handleResponse : function(response){
43099         if(this.form.reader){
43100             var rs = this.form.reader.read(response);
43101             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
43102             return {
43103                 success : rs.success,
43104                 data : data
43105             };
43106         }
43107         return Roo.decode(response.responseText);
43108     }
43109 });
43110
43111 Roo.form.Action.ACTION_TYPES = {
43112     'load' : Roo.form.Action.Load,
43113     'submit' : Roo.form.Action.Submit
43114 };/*
43115  * Based on:
43116  * Ext JS Library 1.1.1
43117  * Copyright(c) 2006-2007, Ext JS, LLC.
43118  *
43119  * Originally Released Under LGPL - original licence link has changed is not relivant.
43120  *
43121  * Fork - LGPL
43122  * <script type="text/javascript">
43123  */
43124  
43125 /**
43126  * @class Roo.form.Layout
43127  * @extends Roo.Component
43128  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
43129  * @constructor
43130  * @param {Object} config Configuration options
43131  */
43132 Roo.form.Layout = function(config){
43133     var xitems = [];
43134     if (config.items) {
43135         xitems = config.items;
43136         delete config.items;
43137     }
43138     Roo.form.Layout.superclass.constructor.call(this, config);
43139     this.stack = [];
43140     Roo.each(xitems, this.addxtype, this);
43141      
43142 };
43143
43144 Roo.extend(Roo.form.Layout, Roo.Component, {
43145     /**
43146      * @cfg {String/Object} autoCreate
43147      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
43148      */
43149     /**
43150      * @cfg {String/Object/Function} style
43151      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
43152      * a function which returns such a specification.
43153      */
43154     /**
43155      * @cfg {String} labelAlign
43156      * Valid values are "left," "top" and "right" (defaults to "left")
43157      */
43158     /**
43159      * @cfg {Number} labelWidth
43160      * Fixed width in pixels of all field labels (defaults to undefined)
43161      */
43162     /**
43163      * @cfg {Boolean} clear
43164      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
43165      */
43166     clear : true,
43167     /**
43168      * @cfg {String} labelSeparator
43169      * The separator to use after field labels (defaults to ':')
43170      */
43171     labelSeparator : ':',
43172     /**
43173      * @cfg {Boolean} hideLabels
43174      * True to suppress the display of field labels in this layout (defaults to false)
43175      */
43176     hideLabels : false,
43177
43178     // private
43179     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
43180     
43181     isLayout : true,
43182     
43183     // private
43184     onRender : function(ct, position){
43185         if(this.el){ // from markup
43186             this.el = Roo.get(this.el);
43187         }else {  // generate
43188             var cfg = this.getAutoCreate();
43189             this.el = ct.createChild(cfg, position);
43190         }
43191         if(this.style){
43192             this.el.applyStyles(this.style);
43193         }
43194         if(this.labelAlign){
43195             this.el.addClass('x-form-label-'+this.labelAlign);
43196         }
43197         if(this.hideLabels){
43198             this.labelStyle = "display:none";
43199             this.elementStyle = "padding-left:0;";
43200         }else{
43201             if(typeof this.labelWidth == 'number'){
43202                 this.labelStyle = "width:"+this.labelWidth+"px;";
43203                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
43204             }
43205             if(this.labelAlign == 'top'){
43206                 this.labelStyle = "width:auto;";
43207                 this.elementStyle = "padding-left:0;";
43208             }
43209         }
43210         var stack = this.stack;
43211         var slen = stack.length;
43212         if(slen > 0){
43213             if(!this.fieldTpl){
43214                 var t = new Roo.Template(
43215                     '<div class="x-form-item {5}">',
43216                         '<label for="{0}" style="{2}">{1}{4}</label>',
43217                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43218                         '</div>',
43219                     '</div><div class="x-form-clear-left"></div>'
43220                 );
43221                 t.disableFormats = true;
43222                 t.compile();
43223                 Roo.form.Layout.prototype.fieldTpl = t;
43224             }
43225             for(var i = 0; i < slen; i++) {
43226                 if(stack[i].isFormField){
43227                     this.renderField(stack[i]);
43228                 }else{
43229                     this.renderComponent(stack[i]);
43230                 }
43231             }
43232         }
43233         if(this.clear){
43234             this.el.createChild({cls:'x-form-clear'});
43235         }
43236     },
43237
43238     // private
43239     renderField : function(f){
43240         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
43241                f.id, //0
43242                f.fieldLabel, //1
43243                f.labelStyle||this.labelStyle||'', //2
43244                this.elementStyle||'', //3
43245                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
43246                f.itemCls||this.itemCls||''  //5
43247        ], true).getPrevSibling());
43248     },
43249
43250     // private
43251     renderComponent : function(c){
43252         c.render(c.isLayout ? this.el : this.el.createChild());    
43253     },
43254     /**
43255      * Adds a object form elements (using the xtype property as the factory method.)
43256      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
43257      * @param {Object} config 
43258      */
43259     addxtype : function(o)
43260     {
43261         // create the lement.
43262         o.form = this.form;
43263         var fe = Roo.factory(o, Roo.form);
43264         this.form.allItems.push(fe);
43265         this.stack.push(fe);
43266         
43267         if (fe.isFormField) {
43268             this.form.items.add(fe);
43269         }
43270          
43271         return fe;
43272     }
43273 });
43274
43275 /**
43276  * @class Roo.form.Column
43277  * @extends Roo.form.Layout
43278  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
43279  * @constructor
43280  * @param {Object} config Configuration options
43281  */
43282 Roo.form.Column = function(config){
43283     Roo.form.Column.superclass.constructor.call(this, config);
43284 };
43285
43286 Roo.extend(Roo.form.Column, Roo.form.Layout, {
43287     /**
43288      * @cfg {Number/String} width
43289      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43290      */
43291     /**
43292      * @cfg {String/Object} autoCreate
43293      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
43294      */
43295
43296     // private
43297     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
43298
43299     // private
43300     onRender : function(ct, position){
43301         Roo.form.Column.superclass.onRender.call(this, ct, position);
43302         if(this.width){
43303             this.el.setWidth(this.width);
43304         }
43305     }
43306 });
43307
43308
43309 /**
43310  * @class Roo.form.Row
43311  * @extends Roo.form.Layout
43312  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
43313  * @constructor
43314  * @param {Object} config Configuration options
43315  */
43316
43317  
43318 Roo.form.Row = function(config){
43319     Roo.form.Row.superclass.constructor.call(this, config);
43320 };
43321  
43322 Roo.extend(Roo.form.Row, Roo.form.Layout, {
43323       /**
43324      * @cfg {Number/String} width
43325      * The fixed width of the column in pixels or CSS value (defaults to "auto")
43326      */
43327     /**
43328      * @cfg {Number/String} height
43329      * The fixed height of the column in pixels or CSS value (defaults to "auto")
43330      */
43331     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
43332     
43333     padWidth : 20,
43334     // private
43335     onRender : function(ct, position){
43336         //console.log('row render');
43337         if(!this.rowTpl){
43338             var t = new Roo.Template(
43339                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
43340                     '<label for="{0}" style="{2}">{1}{4}</label>',
43341                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
43342                     '</div>',
43343                 '</div>'
43344             );
43345             t.disableFormats = true;
43346             t.compile();
43347             Roo.form.Layout.prototype.rowTpl = t;
43348         }
43349         this.fieldTpl = this.rowTpl;
43350         
43351         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
43352         var labelWidth = 100;
43353         
43354         if ((this.labelAlign != 'top')) {
43355             if (typeof this.labelWidth == 'number') {
43356                 labelWidth = this.labelWidth
43357             }
43358             this.padWidth =  20 + labelWidth;
43359             
43360         }
43361         
43362         Roo.form.Column.superclass.onRender.call(this, ct, position);
43363         if(this.width){
43364             this.el.setWidth(this.width);
43365         }
43366         if(this.height){
43367             this.el.setHeight(this.height);
43368         }
43369     },
43370     
43371     // private
43372     renderField : function(f){
43373         f.fieldEl = this.fieldTpl.append(this.el, [
43374                f.id, f.fieldLabel,
43375                f.labelStyle||this.labelStyle||'',
43376                this.elementStyle||'',
43377                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
43378                f.itemCls||this.itemCls||'',
43379                f.width ? f.width + this.padWidth : 160 + this.padWidth
43380        ],true);
43381     }
43382 });
43383  
43384
43385 /**
43386  * @class Roo.form.FieldSet
43387  * @extends Roo.form.Layout
43388  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
43389  * @constructor
43390  * @param {Object} config Configuration options
43391  */
43392 Roo.form.FieldSet = function(config){
43393     Roo.form.FieldSet.superclass.constructor.call(this, config);
43394 };
43395
43396 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
43397     /**
43398      * @cfg {String} legend
43399      * The text to display as the legend for the FieldSet (defaults to '')
43400      */
43401     /**
43402      * @cfg {String/Object} autoCreate
43403      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
43404      */
43405
43406     // private
43407     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
43408
43409     // private
43410     onRender : function(ct, position){
43411         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
43412         if(this.legend){
43413             this.setLegend(this.legend);
43414         }
43415     },
43416
43417     // private
43418     setLegend : function(text){
43419         if(this.rendered){
43420             this.el.child('legend').update(text);
43421         }
43422     }
43423 });/*
43424  * Based on:
43425  * Ext JS Library 1.1.1
43426  * Copyright(c) 2006-2007, Ext JS, LLC.
43427  *
43428  * Originally Released Under LGPL - original licence link has changed is not relivant.
43429  *
43430  * Fork - LGPL
43431  * <script type="text/javascript">
43432  */
43433 /**
43434  * @class Roo.form.VTypes
43435  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
43436  * @singleton
43437  */
43438 Roo.form.VTypes = function(){
43439     // closure these in so they are only created once.
43440     var alpha = /^[a-zA-Z_]+$/;
43441     var alphanum = /^[a-zA-Z0-9_]+$/;
43442     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
43443     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
43444
43445     // All these messages and functions are configurable
43446     return {
43447         /**
43448          * The function used to validate email addresses
43449          * @param {String} value The email address
43450          */
43451         'email' : function(v){
43452             return email.test(v);
43453         },
43454         /**
43455          * The error text to display when the email validation function returns false
43456          * @type String
43457          */
43458         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
43459         /**
43460          * The keystroke filter mask to be applied on email input
43461          * @type RegExp
43462          */
43463         'emailMask' : /[a-z0-9_\.\-@]/i,
43464
43465         /**
43466          * The function used to validate URLs
43467          * @param {String} value The URL
43468          */
43469         'url' : function(v){
43470             return url.test(v);
43471         },
43472         /**
43473          * The error text to display when the url validation function returns false
43474          * @type String
43475          */
43476         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
43477         
43478         /**
43479          * The function used to validate alpha values
43480          * @param {String} value The value
43481          */
43482         'alpha' : function(v){
43483             return alpha.test(v);
43484         },
43485         /**
43486          * The error text to display when the alpha validation function returns false
43487          * @type String
43488          */
43489         'alphaText' : 'This field should only contain letters and _',
43490         /**
43491          * The keystroke filter mask to be applied on alpha input
43492          * @type RegExp
43493          */
43494         'alphaMask' : /[a-z_]/i,
43495
43496         /**
43497          * The function used to validate alphanumeric values
43498          * @param {String} value The value
43499          */
43500         'alphanum' : function(v){
43501             return alphanum.test(v);
43502         },
43503         /**
43504          * The error text to display when the alphanumeric validation function returns false
43505          * @type String
43506          */
43507         'alphanumText' : 'This field should only contain letters, numbers and _',
43508         /**
43509          * The keystroke filter mask to be applied on alphanumeric input
43510          * @type RegExp
43511          */
43512         'alphanumMask' : /[a-z0-9_]/i
43513     };
43514 }();//<script type="text/javascript">
43515
43516 /**
43517  * @class Roo.form.FCKeditor
43518  * @extends Roo.form.TextArea
43519  * Wrapper around the FCKEditor http://www.fckeditor.net
43520  * @constructor
43521  * Creates a new FCKeditor
43522  * @param {Object} config Configuration options
43523  */
43524 Roo.form.FCKeditor = function(config){
43525     Roo.form.FCKeditor.superclass.constructor.call(this, config);
43526     this.addEvents({
43527          /**
43528          * @event editorinit
43529          * Fired when the editor is initialized - you can add extra handlers here..
43530          * @param {FCKeditor} this
43531          * @param {Object} the FCK object.
43532          */
43533         editorinit : true
43534     });
43535     
43536     
43537 };
43538 Roo.form.FCKeditor.editors = { };
43539 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
43540 {
43541     //defaultAutoCreate : {
43542     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
43543     //},
43544     // private
43545     /**
43546      * @cfg {Object} fck options - see fck manual for details.
43547      */
43548     fckconfig : false,
43549     
43550     /**
43551      * @cfg {Object} fck toolbar set (Basic or Default)
43552      */
43553     toolbarSet : 'Basic',
43554     /**
43555      * @cfg {Object} fck BasePath
43556      */ 
43557     basePath : '/fckeditor/',
43558     
43559     
43560     frame : false,
43561     
43562     value : '',
43563     
43564    
43565     onRender : function(ct, position)
43566     {
43567         if(!this.el){
43568             this.defaultAutoCreate = {
43569                 tag: "textarea",
43570                 style:"width:300px;height:60px;",
43571                 autocomplete: "off"
43572             };
43573         }
43574         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
43575         /*
43576         if(this.grow){
43577             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43578             if(this.preventScrollbars){
43579                 this.el.setStyle("overflow", "hidden");
43580             }
43581             this.el.setHeight(this.growMin);
43582         }
43583         */
43584         //console.log('onrender' + this.getId() );
43585         Roo.form.FCKeditor.editors[this.getId()] = this;
43586          
43587
43588         this.replaceTextarea() ;
43589         
43590     },
43591     
43592     getEditor : function() {
43593         return this.fckEditor;
43594     },
43595     /**
43596      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43597      * @param {Mixed} value The value to set
43598      */
43599     
43600     
43601     setValue : function(value)
43602     {
43603         //console.log('setValue: ' + value);
43604         
43605         if(typeof(value) == 'undefined') { // not sure why this is happending...
43606             return;
43607         }
43608         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43609         
43610         //if(!this.el || !this.getEditor()) {
43611         //    this.value = value;
43612             //this.setValue.defer(100,this,[value]);    
43613         //    return;
43614         //} 
43615         
43616         if(!this.getEditor()) {
43617             return;
43618         }
43619         
43620         this.getEditor().SetData(value);
43621         
43622         //
43623
43624     },
43625
43626     /**
43627      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43628      * @return {Mixed} value The field value
43629      */
43630     getValue : function()
43631     {
43632         
43633         if (this.frame && this.frame.dom.style.display == 'none') {
43634             return Roo.form.FCKeditor.superclass.getValue.call(this);
43635         }
43636         
43637         if(!this.el || !this.getEditor()) {
43638            
43639            // this.getValue.defer(100,this); 
43640             return this.value;
43641         }
43642        
43643         
43644         var value=this.getEditor().GetData();
43645         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43646         return Roo.form.FCKeditor.superclass.getValue.call(this);
43647         
43648
43649     },
43650
43651     /**
43652      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43653      * @return {Mixed} value The field value
43654      */
43655     getRawValue : function()
43656     {
43657         if (this.frame && this.frame.dom.style.display == 'none') {
43658             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43659         }
43660         
43661         if(!this.el || !this.getEditor()) {
43662             //this.getRawValue.defer(100,this); 
43663             return this.value;
43664             return;
43665         }
43666         
43667         
43668         
43669         var value=this.getEditor().GetData();
43670         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43671         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43672          
43673     },
43674     
43675     setSize : function(w,h) {
43676         
43677         
43678         
43679         //if (this.frame && this.frame.dom.style.display == 'none') {
43680         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43681         //    return;
43682         //}
43683         //if(!this.el || !this.getEditor()) {
43684         //    this.setSize.defer(100,this, [w,h]); 
43685         //    return;
43686         //}
43687         
43688         
43689         
43690         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43691         
43692         this.frame.dom.setAttribute('width', w);
43693         this.frame.dom.setAttribute('height', h);
43694         this.frame.setSize(w,h);
43695         
43696     },
43697     
43698     toggleSourceEdit : function(value) {
43699         
43700       
43701          
43702         this.el.dom.style.display = value ? '' : 'none';
43703         this.frame.dom.style.display = value ?  'none' : '';
43704         
43705     },
43706     
43707     
43708     focus: function(tag)
43709     {
43710         if (this.frame.dom.style.display == 'none') {
43711             return Roo.form.FCKeditor.superclass.focus.call(this);
43712         }
43713         if(!this.el || !this.getEditor()) {
43714             this.focus.defer(100,this, [tag]); 
43715             return;
43716         }
43717         
43718         
43719         
43720         
43721         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43722         this.getEditor().Focus();
43723         if (tgs.length) {
43724             if (!this.getEditor().Selection.GetSelection()) {
43725                 this.focus.defer(100,this, [tag]); 
43726                 return;
43727             }
43728             
43729             
43730             var r = this.getEditor().EditorDocument.createRange();
43731             r.setStart(tgs[0],0);
43732             r.setEnd(tgs[0],0);
43733             this.getEditor().Selection.GetSelection().removeAllRanges();
43734             this.getEditor().Selection.GetSelection().addRange(r);
43735             this.getEditor().Focus();
43736         }
43737         
43738     },
43739     
43740     
43741     
43742     replaceTextarea : function()
43743     {
43744         if ( document.getElementById( this.getId() + '___Frame' ) )
43745             return ;
43746         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43747         //{
43748             // We must check the elements firstly using the Id and then the name.
43749         var oTextarea = document.getElementById( this.getId() );
43750         
43751         var colElementsByName = document.getElementsByName( this.getId() ) ;
43752          
43753         oTextarea.style.display = 'none' ;
43754
43755         if ( oTextarea.tabIndex ) {            
43756             this.TabIndex = oTextarea.tabIndex ;
43757         }
43758         
43759         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43760         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43761         this.frame = Roo.get(this.getId() + '___Frame')
43762     },
43763     
43764     _getConfigHtml : function()
43765     {
43766         var sConfig = '' ;
43767
43768         for ( var o in this.fckconfig ) {
43769             sConfig += sConfig.length > 0  ? '&amp;' : '';
43770             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43771         }
43772
43773         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43774     },
43775     
43776     
43777     _getIFrameHtml : function()
43778     {
43779         var sFile = 'fckeditor.html' ;
43780         /* no idea what this is about..
43781         try
43782         {
43783             if ( (/fcksource=true/i).test( window.top.location.search ) )
43784                 sFile = 'fckeditor.original.html' ;
43785         }
43786         catch (e) { 
43787         */
43788
43789         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43790         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43791         
43792         
43793         var html = '<iframe id="' + this.getId() +
43794             '___Frame" src="' + sLink +
43795             '" width="' + this.width +
43796             '" height="' + this.height + '"' +
43797             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43798             ' frameborder="0" scrolling="no"></iframe>' ;
43799
43800         return html ;
43801     },
43802     
43803     _insertHtmlBefore : function( html, element )
43804     {
43805         if ( element.insertAdjacentHTML )       {
43806             // IE
43807             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43808         } else { // Gecko
43809             var oRange = document.createRange() ;
43810             oRange.setStartBefore( element ) ;
43811             var oFragment = oRange.createContextualFragment( html );
43812             element.parentNode.insertBefore( oFragment, element ) ;
43813         }
43814     }
43815     
43816     
43817   
43818     
43819     
43820     
43821     
43822
43823 });
43824
43825 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43826
43827 function FCKeditor_OnComplete(editorInstance){
43828     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43829     f.fckEditor = editorInstance;
43830     //console.log("loaded");
43831     f.fireEvent('editorinit', f, editorInstance);
43832
43833   
43834
43835  
43836
43837
43838
43839
43840
43841
43842
43843
43844
43845
43846
43847
43848
43849
43850
43851 //<script type="text/javascript">
43852 /**
43853  * @class Roo.form.GridField
43854  * @extends Roo.form.Field
43855  * Embed a grid (or editable grid into a form)
43856  * STATUS ALPHA
43857  * 
43858  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43859  * it needs 
43860  * xgrid.store = Roo.data.Store
43861  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43862  * xgrid.store.reader = Roo.data.JsonReader 
43863  * 
43864  * 
43865  * @constructor
43866  * Creates a new GridField
43867  * @param {Object} config Configuration options
43868  */
43869 Roo.form.GridField = function(config){
43870     Roo.form.GridField.superclass.constructor.call(this, config);
43871      
43872 };
43873
43874 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43875     /**
43876      * @cfg {Number} width  - used to restrict width of grid..
43877      */
43878     width : 100,
43879     /**
43880      * @cfg {Number} height - used to restrict height of grid..
43881      */
43882     height : 50,
43883      /**
43884      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43885          * 
43886          *}
43887      */
43888     xgrid : false, 
43889     /**
43890      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43891      * {tag: "input", type: "checkbox", autocomplete: "off"})
43892      */
43893    // defaultAutoCreate : { tag: 'div' },
43894     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43895     /**
43896      * @cfg {String} addTitle Text to include for adding a title.
43897      */
43898     addTitle : false,
43899     //
43900     onResize : function(){
43901         Roo.form.Field.superclass.onResize.apply(this, arguments);
43902     },
43903
43904     initEvents : function(){
43905         // Roo.form.Checkbox.superclass.initEvents.call(this);
43906         // has no events...
43907        
43908     },
43909
43910
43911     getResizeEl : function(){
43912         return this.wrap;
43913     },
43914
43915     getPositionEl : function(){
43916         return this.wrap;
43917     },
43918
43919     // private
43920     onRender : function(ct, position){
43921         
43922         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43923         var style = this.style;
43924         delete this.style;
43925         
43926         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43927         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43928         this.viewEl = this.wrap.createChild({ tag: 'div' });
43929         if (style) {
43930             this.viewEl.applyStyles(style);
43931         }
43932         if (this.width) {
43933             this.viewEl.setWidth(this.width);
43934         }
43935         if (this.height) {
43936             this.viewEl.setHeight(this.height);
43937         }
43938         //if(this.inputValue !== undefined){
43939         //this.setValue(this.value);
43940         
43941         
43942         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43943         
43944         
43945         this.grid.render();
43946         this.grid.getDataSource().on('remove', this.refreshValue, this);
43947         this.grid.getDataSource().on('update', this.refreshValue, this);
43948         this.grid.on('afteredit', this.refreshValue, this);
43949  
43950     },
43951      
43952     
43953     /**
43954      * Sets the value of the item. 
43955      * @param {String} either an object  or a string..
43956      */
43957     setValue : function(v){
43958         //this.value = v;
43959         v = v || []; // empty set..
43960         // this does not seem smart - it really only affects memoryproxy grids..
43961         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43962             var ds = this.grid.getDataSource();
43963             // assumes a json reader..
43964             var data = {}
43965             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43966             ds.loadData( data);
43967         }
43968         // clear selection so it does not get stale.
43969         if (this.grid.sm) { 
43970             this.grid.sm.clearSelections();
43971         }
43972         
43973         Roo.form.GridField.superclass.setValue.call(this, v);
43974         this.refreshValue();
43975         // should load data in the grid really....
43976     },
43977     
43978     // private
43979     refreshValue: function() {
43980          var val = [];
43981         this.grid.getDataSource().each(function(r) {
43982             val.push(r.data);
43983         });
43984         this.el.dom.value = Roo.encode(val);
43985     }
43986     
43987      
43988     
43989     
43990 });/*
43991  * Based on:
43992  * Ext JS Library 1.1.1
43993  * Copyright(c) 2006-2007, Ext JS, LLC.
43994  *
43995  * Originally Released Under LGPL - original licence link has changed is not relivant.
43996  *
43997  * Fork - LGPL
43998  * <script type="text/javascript">
43999  */
44000 /**
44001  * @class Roo.form.DisplayField
44002  * @extends Roo.form.Field
44003  * A generic Field to display non-editable data.
44004  * @constructor
44005  * Creates a new Display Field item.
44006  * @param {Object} config Configuration options
44007  */
44008 Roo.form.DisplayField = function(config){
44009     Roo.form.DisplayField.superclass.constructor.call(this, config);
44010     
44011 };
44012
44013 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
44014     inputType:      'hidden',
44015     allowBlank:     true,
44016     readOnly:         true,
44017     
44018  
44019     /**
44020      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44021      */
44022     focusClass : undefined,
44023     /**
44024      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44025      */
44026     fieldClass: 'x-form-field',
44027     
44028      /**
44029      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
44030      */
44031     valueRenderer: undefined,
44032     
44033     width: 100,
44034     /**
44035      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44036      * {tag: "input", type: "checkbox", autocomplete: "off"})
44037      */
44038      
44039  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
44040
44041     onResize : function(){
44042         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
44043         
44044     },
44045
44046     initEvents : function(){
44047         // Roo.form.Checkbox.superclass.initEvents.call(this);
44048         // has no events...
44049        
44050     },
44051
44052
44053     getResizeEl : function(){
44054         return this.wrap;
44055     },
44056
44057     getPositionEl : function(){
44058         return this.wrap;
44059     },
44060
44061     // private
44062     onRender : function(ct, position){
44063         
44064         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
44065         //if(this.inputValue !== undefined){
44066         this.wrap = this.el.wrap();
44067         
44068         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
44069         
44070         if (this.bodyStyle) {
44071             this.viewEl.applyStyles(this.bodyStyle);
44072         }
44073         //this.viewEl.setStyle('padding', '2px');
44074         
44075         this.setValue(this.value);
44076         
44077     },
44078 /*
44079     // private
44080     initValue : Roo.emptyFn,
44081
44082   */
44083
44084         // private
44085     onClick : function(){
44086         
44087     },
44088
44089     /**
44090      * Sets the checked state of the checkbox.
44091      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
44092      */
44093     setValue : function(v){
44094         this.value = v;
44095         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
44096         // this might be called before we have a dom element..
44097         if (!this.viewEl) {
44098             return;
44099         }
44100         this.viewEl.dom.innerHTML = html;
44101         Roo.form.DisplayField.superclass.setValue.call(this, v);
44102
44103     }
44104 });/*
44105  * 
44106  * Licence- LGPL
44107  * 
44108  */
44109
44110 /**
44111  * @class Roo.form.DayPicker
44112  * @extends Roo.form.Field
44113  * A Day picker show [M] [T] [W] ....
44114  * @constructor
44115  * Creates a new Day Picker
44116  * @param {Object} config Configuration options
44117  */
44118 Roo.form.DayPicker= function(config){
44119     Roo.form.DayPicker.superclass.constructor.call(this, config);
44120      
44121 };
44122
44123 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
44124     /**
44125      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44126      */
44127     focusClass : undefined,
44128     /**
44129      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44130      */
44131     fieldClass: "x-form-field",
44132    
44133     /**
44134      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44135      * {tag: "input", type: "checkbox", autocomplete: "off"})
44136      */
44137     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44138     
44139    
44140     actionMode : 'viewEl', 
44141     //
44142     // private
44143  
44144     inputType : 'hidden',
44145     
44146      
44147     inputElement: false, // real input element?
44148     basedOn: false, // ????
44149     
44150     isFormField: true, // not sure where this is needed!!!!
44151
44152     onResize : function(){
44153         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44154         if(!this.boxLabel){
44155             this.el.alignTo(this.wrap, 'c-c');
44156         }
44157     },
44158
44159     initEvents : function(){
44160         Roo.form.Checkbox.superclass.initEvents.call(this);
44161         this.el.on("click", this.onClick,  this);
44162         this.el.on("change", this.onClick,  this);
44163     },
44164
44165
44166     getResizeEl : function(){
44167         return this.wrap;
44168     },
44169
44170     getPositionEl : function(){
44171         return this.wrap;
44172     },
44173
44174     
44175     // private
44176     onRender : function(ct, position){
44177         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44178        
44179         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
44180         
44181         var r1 = '<table><tr>';
44182         var r2 = '<tr class="x-form-daypick-icons">';
44183         for (var i=0; i < 7; i++) {
44184             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
44185             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
44186         }
44187         
44188         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
44189         viewEl.select('img').on('click', this.onClick, this);
44190         this.viewEl = viewEl;   
44191         
44192         
44193         // this will not work on Chrome!!!
44194         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44195         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44196         
44197         
44198           
44199
44200     },
44201
44202     // private
44203     initValue : Roo.emptyFn,
44204
44205     /**
44206      * Returns the checked state of the checkbox.
44207      * @return {Boolean} True if checked, else false
44208      */
44209     getValue : function(){
44210         return this.el.dom.value;
44211         
44212     },
44213
44214         // private
44215     onClick : function(e){ 
44216         //this.setChecked(!this.checked);
44217         Roo.get(e.target).toggleClass('x-menu-item-checked');
44218         this.refreshValue();
44219         //if(this.el.dom.checked != this.checked){
44220         //    this.setValue(this.el.dom.checked);
44221        // }
44222     },
44223     
44224     // private
44225     refreshValue : function()
44226     {
44227         var val = '';
44228         this.viewEl.select('img',true).each(function(e,i,n)  {
44229             val += e.is(".x-menu-item-checked") ? String(n) : '';
44230         });
44231         this.setValue(val, true);
44232     },
44233
44234     /**
44235      * Sets the checked state of the checkbox.
44236      * On is always based on a string comparison between inputValue and the param.
44237      * @param {Boolean/String} value - the value to set 
44238      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44239      */
44240     setValue : function(v,suppressEvent){
44241         if (!this.el.dom) {
44242             return;
44243         }
44244         var old = this.el.dom.value ;
44245         this.el.dom.value = v;
44246         if (suppressEvent) {
44247             return ;
44248         }
44249          
44250         // update display..
44251         this.viewEl.select('img',true).each(function(e,i,n)  {
44252             
44253             var on = e.is(".x-menu-item-checked");
44254             var newv = v.indexOf(String(n)) > -1;
44255             if (on != newv) {
44256                 e.toggleClass('x-menu-item-checked');
44257             }
44258             
44259         });
44260         
44261         
44262         this.fireEvent('change', this, v, old);
44263         
44264         
44265     },
44266    
44267     // handle setting of hidden value by some other method!!?!?
44268     setFromHidden: function()
44269     {
44270         if(!this.el){
44271             return;
44272         }
44273         //console.log("SET FROM HIDDEN");
44274         //alert('setFrom hidden');
44275         this.setValue(this.el.dom.value);
44276     },
44277     
44278     onDestroy : function()
44279     {
44280         if(this.viewEl){
44281             Roo.get(this.viewEl).remove();
44282         }
44283          
44284         Roo.form.DayPicker.superclass.onDestroy.call(this);
44285     }
44286
44287 });/*
44288  * RooJS Library 1.1.1
44289  * Copyright(c) 2008-2011  Alan Knowles
44290  *
44291  * License - LGPL
44292  */
44293  
44294
44295 /**
44296  * @class Roo.form.ComboCheck
44297  * @extends Roo.form.ComboBox
44298  * A combobox for multiple select items.
44299  *
44300  * FIXME - could do with a reset button..
44301  * 
44302  * @constructor
44303  * Create a new ComboCheck
44304  * @param {Object} config Configuration options
44305  */
44306 Roo.form.ComboCheck = function(config){
44307     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44308     // should verify some data...
44309     // like
44310     // hiddenName = required..
44311     // displayField = required
44312     // valudField == required
44313     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44314     var _t = this;
44315     Roo.each(req, function(e) {
44316         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44317             throw "Roo.form.ComboCheck : missing value for: " + e;
44318         }
44319     });
44320     
44321     
44322 };
44323
44324 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
44325      
44326      
44327     editable : false,
44328      
44329     selectedClass: 'x-menu-item-checked', 
44330     
44331     // private
44332     onRender : function(ct, position){
44333         var _t = this;
44334         
44335         
44336         
44337         if(!this.tpl){
44338             var cls = 'x-combo-list';
44339
44340             
44341             this.tpl =  new Roo.Template({
44342                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
44343                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
44344                    '<span>{' + this.displayField + '}</span>' +
44345                     '</div>' 
44346                 
44347             });
44348         }
44349  
44350         
44351         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
44352         this.view.singleSelect = false;
44353         this.view.multiSelect = true;
44354         this.view.toggleSelect = true;
44355         this.pageTb.add(new Roo.Toolbar.Fill(), {
44356             
44357             text: 'Done',
44358             handler: function()
44359             {
44360                 _t.collapse();
44361             }
44362         });
44363     },
44364     
44365     onViewOver : function(e, t){
44366         // do nothing...
44367         return;
44368         
44369     },
44370     
44371     onViewClick : function(doFocus,index){
44372         return;
44373         
44374     },
44375     select: function () {
44376         //Roo.log("SELECT CALLED");
44377     },
44378      
44379     selectByValue : function(xv, scrollIntoView){
44380         var ar = this.getValueArray();
44381         var sels = [];
44382         
44383         Roo.each(ar, function(v) {
44384             if(v === undefined || v === null){
44385                 return;
44386             }
44387             var r = this.findRecord(this.valueField, v);
44388             if(r){
44389                 sels.push(this.store.indexOf(r))
44390                 
44391             }
44392         },this);
44393         this.view.select(sels);
44394         return false;
44395     },
44396     
44397     
44398     
44399     onSelect : function(record, index){
44400        // Roo.log("onselect Called");
44401        // this is only called by the clear button now..
44402         this.view.clearSelections();
44403         this.setValue('[]');
44404         if (this.value != this.valueBefore) {
44405             this.fireEvent('change', this, this.value, this.valueBefore);
44406         }
44407     },
44408     getValueArray : function()
44409     {
44410         var ar = [] ;
44411         
44412         try {
44413             //Roo.log(this.value);
44414             if (typeof(this.value) == 'undefined') {
44415                 return [];
44416             }
44417             var ar = Roo.decode(this.value);
44418             return  ar instanceof Array ? ar : []; //?? valid?
44419             
44420         } catch(e) {
44421             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
44422             return [];
44423         }
44424          
44425     },
44426     expand : function ()
44427     {
44428         Roo.form.ComboCheck.superclass.expand.call(this);
44429         this.valueBefore = this.value;
44430         
44431
44432     },
44433     
44434     collapse : function(){
44435         Roo.form.ComboCheck.superclass.collapse.call(this);
44436         var sl = this.view.getSelectedIndexes();
44437         var st = this.store;
44438         var nv = [];
44439         var tv = [];
44440         var r;
44441         Roo.each(sl, function(i) {
44442             r = st.getAt(i);
44443             nv.push(r.get(this.valueField));
44444         },this);
44445         this.setValue(Roo.encode(nv));
44446         if (this.value != this.valueBefore) {
44447
44448             this.fireEvent('change', this, this.value, this.valueBefore);
44449         }
44450         
44451     },
44452     
44453     setValue : function(v){
44454         // Roo.log(v);
44455         this.value = v;
44456         
44457         var vals = this.getValueArray();
44458         var tv = [];
44459         Roo.each(vals, function(k) {
44460             var r = this.findRecord(this.valueField, k);
44461             if(r){
44462                 tv.push(r.data[this.displayField]);
44463             }else if(this.valueNotFoundText !== undefined){
44464                 tv.push( this.valueNotFoundText );
44465             }
44466         },this);
44467        // Roo.log(tv);
44468         
44469         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
44470         this.hiddenField.value = v;
44471         this.value = v;
44472     }
44473     
44474 });//<script type="text/javasscript">
44475  
44476
44477 /**
44478  * @class Roo.DDView
44479  * A DnD enabled version of Roo.View.
44480  * @param {Element/String} container The Element in which to create the View.
44481  * @param {String} tpl The template string used to create the markup for each element of the View
44482  * @param {Object} config The configuration properties. These include all the config options of
44483  * {@link Roo.View} plus some specific to this class.<br>
44484  * <p>
44485  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
44486  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
44487  * <p>
44488  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
44489 .x-view-drag-insert-above {
44490         border-top:1px dotted #3366cc;
44491 }
44492 .x-view-drag-insert-below {
44493         border-bottom:1px dotted #3366cc;
44494 }
44495 </code></pre>
44496  * 
44497  */
44498  
44499 Roo.DDView = function(container, tpl, config) {
44500     Roo.DDView.superclass.constructor.apply(this, arguments);
44501     this.getEl().setStyle("outline", "0px none");
44502     this.getEl().unselectable();
44503     if (this.dragGroup) {
44504                 this.setDraggable(this.dragGroup.split(","));
44505     }
44506     if (this.dropGroup) {
44507                 this.setDroppable(this.dropGroup.split(","));
44508     }
44509     if (this.deletable) {
44510         this.setDeletable();
44511     }
44512     this.isDirtyFlag = false;
44513         this.addEvents({
44514                 "drop" : true
44515         });
44516 };
44517
44518 Roo.extend(Roo.DDView, Roo.View, {
44519 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
44520 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
44521 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
44522 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
44523
44524         isFormField: true,
44525
44526         reset: Roo.emptyFn,
44527         
44528         clearInvalid: Roo.form.Field.prototype.clearInvalid,
44529
44530         validate: function() {
44531                 return true;
44532         },
44533         
44534         destroy: function() {
44535                 this.purgeListeners();
44536                 this.getEl.removeAllListeners();
44537                 this.getEl().remove();
44538                 if (this.dragZone) {
44539                         if (this.dragZone.destroy) {
44540                                 this.dragZone.destroy();
44541                         }
44542                 }
44543                 if (this.dropZone) {
44544                         if (this.dropZone.destroy) {
44545                                 this.dropZone.destroy();
44546                         }
44547                 }
44548         },
44549
44550 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
44551         getName: function() {
44552                 return this.name;
44553         },
44554
44555 /**     Loads the View from a JSON string representing the Records to put into the Store. */
44556         setValue: function(v) {
44557                 if (!this.store) {
44558                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
44559                 }
44560                 var data = {};
44561                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
44562                 this.store.proxy = new Roo.data.MemoryProxy(data);
44563                 this.store.load();
44564         },
44565
44566 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
44567         getValue: function() {
44568                 var result = '(';
44569                 this.store.each(function(rec) {
44570                         result += rec.id + ',';
44571                 });
44572                 return result.substr(0, result.length - 1) + ')';
44573         },
44574         
44575         getIds: function() {
44576                 var i = 0, result = new Array(this.store.getCount());
44577                 this.store.each(function(rec) {
44578                         result[i++] = rec.id;
44579                 });
44580                 return result;
44581         },
44582         
44583         isDirty: function() {
44584                 return this.isDirtyFlag;
44585         },
44586
44587 /**
44588  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44589  *      whole Element becomes the target, and this causes the drop gesture to append.
44590  */
44591     getTargetFromEvent : function(e) {
44592                 var target = e.getTarget();
44593                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44594                 target = target.parentNode;
44595                 }
44596                 if (!target) {
44597                         target = this.el.dom.lastChild || this.el.dom;
44598                 }
44599                 return target;
44600     },
44601
44602 /**
44603  *      Create the drag data which consists of an object which has the property "ddel" as
44604  *      the drag proxy element. 
44605  */
44606     getDragData : function(e) {
44607         var target = this.findItemFromChild(e.getTarget());
44608                 if(target) {
44609                         this.handleSelection(e);
44610                         var selNodes = this.getSelectedNodes();
44611             var dragData = {
44612                 source: this,
44613                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44614                 nodes: selNodes,
44615                 records: []
44616                         };
44617                         var selectedIndices = this.getSelectedIndexes();
44618                         for (var i = 0; i < selectedIndices.length; i++) {
44619                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44620                         }
44621                         if (selNodes.length == 1) {
44622                                 dragData.ddel = target.cloneNode(true); // the div element
44623                         } else {
44624                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44625                                 div.className = 'multi-proxy';
44626                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44627                                         div.appendChild(selNodes[i].cloneNode(true));
44628                                 }
44629                                 dragData.ddel = div;
44630                         }
44631             //console.log(dragData)
44632             //console.log(dragData.ddel.innerHTML)
44633                         return dragData;
44634                 }
44635         //console.log('nodragData')
44636                 return false;
44637     },
44638     
44639 /**     Specify to which ddGroup items in this DDView may be dragged. */
44640     setDraggable: function(ddGroup) {
44641         if (ddGroup instanceof Array) {
44642                 Roo.each(ddGroup, this.setDraggable, this);
44643                 return;
44644         }
44645         if (this.dragZone) {
44646                 this.dragZone.addToGroup(ddGroup);
44647         } else {
44648                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44649                                 containerScroll: true,
44650                                 ddGroup: ddGroup 
44651
44652                         });
44653 //                      Draggability implies selection. DragZone's mousedown selects the element.
44654                         if (!this.multiSelect) { this.singleSelect = true; }
44655
44656 //                      Wire the DragZone's handlers up to methods in *this*
44657                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44658                 }
44659     },
44660
44661 /**     Specify from which ddGroup this DDView accepts drops. */
44662     setDroppable: function(ddGroup) {
44663         if (ddGroup instanceof Array) {
44664                 Roo.each(ddGroup, this.setDroppable, this);
44665                 return;
44666         }
44667         if (this.dropZone) {
44668                 this.dropZone.addToGroup(ddGroup);
44669         } else {
44670                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44671                                 containerScroll: true,
44672                                 ddGroup: ddGroup
44673                         });
44674
44675 //                      Wire the DropZone's handlers up to methods in *this*
44676                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44677                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44678                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44679                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44680                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44681                 }
44682     },
44683
44684 /**     Decide whether to drop above or below a View node. */
44685     getDropPoint : function(e, n, dd){
44686         if (n == this.el.dom) { return "above"; }
44687                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44688                 var c = t + (b - t) / 2;
44689                 var y = Roo.lib.Event.getPageY(e);
44690                 if(y <= c) {
44691                         return "above";
44692                 }else{
44693                         return "below";
44694                 }
44695     },
44696
44697     onNodeEnter : function(n, dd, e, data){
44698                 return false;
44699     },
44700     
44701     onNodeOver : function(n, dd, e, data){
44702                 var pt = this.getDropPoint(e, n, dd);
44703                 // set the insert point style on the target node
44704                 var dragElClass = this.dropNotAllowed;
44705                 if (pt) {
44706                         var targetElClass;
44707                         if (pt == "above"){
44708                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44709                                 targetElClass = "x-view-drag-insert-above";
44710                         } else {
44711                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44712                                 targetElClass = "x-view-drag-insert-below";
44713                         }
44714                         if (this.lastInsertClass != targetElClass){
44715                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44716                                 this.lastInsertClass = targetElClass;
44717                         }
44718                 }
44719                 return dragElClass;
44720         },
44721
44722     onNodeOut : function(n, dd, e, data){
44723                 this.removeDropIndicators(n);
44724     },
44725
44726     onNodeDrop : function(n, dd, e, data){
44727         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44728                 return false;
44729         }
44730         var pt = this.getDropPoint(e, n, dd);
44731                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44732                 if (pt == "below") { insertAt++; }
44733                 for (var i = 0; i < data.records.length; i++) {
44734                         var r = data.records[i];
44735                         var dup = this.store.getById(r.id);
44736                         if (dup && (dd != this.dragZone)) {
44737                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44738                         } else {
44739                                 if (data.copy) {
44740                                         this.store.insert(insertAt++, r.copy());
44741                                 } else {
44742                                         data.source.isDirtyFlag = true;
44743                                         r.store.remove(r);
44744                                         this.store.insert(insertAt++, r);
44745                                 }
44746                                 this.isDirtyFlag = true;
44747                         }
44748                 }
44749                 this.dragZone.cachedTarget = null;
44750                 return true;
44751     },
44752
44753     removeDropIndicators : function(n){
44754                 if(n){
44755                         Roo.fly(n).removeClass([
44756                                 "x-view-drag-insert-above",
44757                                 "x-view-drag-insert-below"]);
44758                         this.lastInsertClass = "_noclass";
44759                 }
44760     },
44761
44762 /**
44763  *      Utility method. Add a delete option to the DDView's context menu.
44764  *      @param {String} imageUrl The URL of the "delete" icon image.
44765  */
44766         setDeletable: function(imageUrl) {
44767                 if (!this.singleSelect && !this.multiSelect) {
44768                         this.singleSelect = true;
44769                 }
44770                 var c = this.getContextMenu();
44771                 this.contextMenu.on("itemclick", function(item) {
44772                         switch (item.id) {
44773                                 case "delete":
44774                                         this.remove(this.getSelectedIndexes());
44775                                         break;
44776                         }
44777                 }, this);
44778                 this.contextMenu.add({
44779                         icon: imageUrl,
44780                         id: "delete",
44781                         text: 'Delete'
44782                 });
44783         },
44784         
44785 /**     Return the context menu for this DDView. */
44786         getContextMenu: function() {
44787                 if (!this.contextMenu) {
44788 //                      Create the View's context menu
44789                         this.contextMenu = new Roo.menu.Menu({
44790                                 id: this.id + "-contextmenu"
44791                         });
44792                         this.el.on("contextmenu", this.showContextMenu, this);
44793                 }
44794                 return this.contextMenu;
44795         },
44796         
44797         disableContextMenu: function() {
44798                 if (this.contextMenu) {
44799                         this.el.un("contextmenu", this.showContextMenu, this);
44800                 }
44801         },
44802
44803         showContextMenu: function(e, item) {
44804         item = this.findItemFromChild(e.getTarget());
44805                 if (item) {
44806                         e.stopEvent();
44807                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44808                         this.contextMenu.showAt(e.getXY());
44809             }
44810     },
44811
44812 /**
44813  *      Remove {@link Roo.data.Record}s at the specified indices.
44814  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44815  */
44816     remove: function(selectedIndices) {
44817                 selectedIndices = [].concat(selectedIndices);
44818                 for (var i = 0; i < selectedIndices.length; i++) {
44819                         var rec = this.store.getAt(selectedIndices[i]);
44820                         this.store.remove(rec);
44821                 }
44822     },
44823
44824 /**
44825  *      Double click fires the event, but also, if this is draggable, and there is only one other
44826  *      related DropZone, it transfers the selected node.
44827  */
44828     onDblClick : function(e){
44829         var item = this.findItemFromChild(e.getTarget());
44830         if(item){
44831             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44832                 return false;
44833             }
44834             if (this.dragGroup) {
44835                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44836                     while (targets.indexOf(this.dropZone) > -1) {
44837                             targets.remove(this.dropZone);
44838                                 }
44839                     if (targets.length == 1) {
44840                                         this.dragZone.cachedTarget = null;
44841                         var el = Roo.get(targets[0].getEl());
44842                         var box = el.getBox(true);
44843                         targets[0].onNodeDrop(el.dom, {
44844                                 target: el.dom,
44845                                 xy: [box.x, box.y + box.height - 1]
44846                         }, null, this.getDragData(e));
44847                     }
44848                 }
44849         }
44850     },
44851     
44852     handleSelection: function(e) {
44853                 this.dragZone.cachedTarget = null;
44854         var item = this.findItemFromChild(e.getTarget());
44855         if (!item) {
44856                 this.clearSelections(true);
44857                 return;
44858         }
44859                 if (item && (this.multiSelect || this.singleSelect)){
44860                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44861                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44862                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44863                                 this.unselect(item);
44864                         } else {
44865                                 this.select(item, this.multiSelect && e.ctrlKey);
44866                                 this.lastSelection = item;
44867                         }
44868                 }
44869     },
44870
44871     onItemClick : function(item, index, e){
44872                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44873                         return false;
44874                 }
44875                 return true;
44876     },
44877
44878     unselect : function(nodeInfo, suppressEvent){
44879                 var node = this.getNode(nodeInfo);
44880                 if(node && this.isSelected(node)){
44881                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44882                                 Roo.fly(node).removeClass(this.selectedClass);
44883                                 this.selections.remove(node);
44884                                 if(!suppressEvent){
44885                                         this.fireEvent("selectionchange", this, this.selections);
44886                                 }
44887                         }
44888                 }
44889     }
44890 });
44891 /*
44892  * Based on:
44893  * Ext JS Library 1.1.1
44894  * Copyright(c) 2006-2007, Ext JS, LLC.
44895  *
44896  * Originally Released Under LGPL - original licence link has changed is not relivant.
44897  *
44898  * Fork - LGPL
44899  * <script type="text/javascript">
44900  */
44901  
44902 /**
44903  * @class Roo.LayoutManager
44904  * @extends Roo.util.Observable
44905  * Base class for layout managers.
44906  */
44907 Roo.LayoutManager = function(container, config){
44908     Roo.LayoutManager.superclass.constructor.call(this);
44909     this.el = Roo.get(container);
44910     // ie scrollbar fix
44911     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44912         document.body.scroll = "no";
44913     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44914         this.el.position('relative');
44915     }
44916     this.id = this.el.id;
44917     this.el.addClass("x-layout-container");
44918     /** false to disable window resize monitoring @type Boolean */
44919     this.monitorWindowResize = true;
44920     this.regions = {};
44921     this.addEvents({
44922         /**
44923          * @event layout
44924          * Fires when a layout is performed. 
44925          * @param {Roo.LayoutManager} this
44926          */
44927         "layout" : true,
44928         /**
44929          * @event regionresized
44930          * Fires when the user resizes a region. 
44931          * @param {Roo.LayoutRegion} region The resized region
44932          * @param {Number} newSize The new size (width for east/west, height for north/south)
44933          */
44934         "regionresized" : true,
44935         /**
44936          * @event regioncollapsed
44937          * Fires when a region is collapsed. 
44938          * @param {Roo.LayoutRegion} region The collapsed region
44939          */
44940         "regioncollapsed" : true,
44941         /**
44942          * @event regionexpanded
44943          * Fires when a region is expanded.  
44944          * @param {Roo.LayoutRegion} region The expanded region
44945          */
44946         "regionexpanded" : true
44947     });
44948     this.updating = false;
44949     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44950 };
44951
44952 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44953     /**
44954      * Returns true if this layout is currently being updated
44955      * @return {Boolean}
44956      */
44957     isUpdating : function(){
44958         return this.updating; 
44959     },
44960     
44961     /**
44962      * Suspend the LayoutManager from doing auto-layouts while
44963      * making multiple add or remove calls
44964      */
44965     beginUpdate : function(){
44966         this.updating = true;    
44967     },
44968     
44969     /**
44970      * Restore auto-layouts and optionally disable the manager from performing a layout
44971      * @param {Boolean} noLayout true to disable a layout update 
44972      */
44973     endUpdate : function(noLayout){
44974         this.updating = false;
44975         if(!noLayout){
44976             this.layout();
44977         }    
44978     },
44979     
44980     layout: function(){
44981         
44982     },
44983     
44984     onRegionResized : function(region, newSize){
44985         this.fireEvent("regionresized", region, newSize);
44986         this.layout();
44987     },
44988     
44989     onRegionCollapsed : function(region){
44990         this.fireEvent("regioncollapsed", region);
44991     },
44992     
44993     onRegionExpanded : function(region){
44994         this.fireEvent("regionexpanded", region);
44995     },
44996         
44997     /**
44998      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44999      * performs box-model adjustments.
45000      * @return {Object} The size as an object {width: (the width), height: (the height)}
45001      */
45002     getViewSize : function(){
45003         var size;
45004         if(this.el.dom != document.body){
45005             size = this.el.getSize();
45006         }else{
45007             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
45008         }
45009         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
45010         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45011         return size;
45012     },
45013     
45014     /**
45015      * Returns the Element this layout is bound to.
45016      * @return {Roo.Element}
45017      */
45018     getEl : function(){
45019         return this.el;
45020     },
45021     
45022     /**
45023      * Returns the specified region.
45024      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
45025      * @return {Roo.LayoutRegion}
45026      */
45027     getRegion : function(target){
45028         return this.regions[target.toLowerCase()];
45029     },
45030     
45031     onWindowResize : function(){
45032         if(this.monitorWindowResize){
45033             this.layout();
45034         }
45035     }
45036 });/*
45037  * Based on:
45038  * Ext JS Library 1.1.1
45039  * Copyright(c) 2006-2007, Ext JS, LLC.
45040  *
45041  * Originally Released Under LGPL - original licence link has changed is not relivant.
45042  *
45043  * Fork - LGPL
45044  * <script type="text/javascript">
45045  */
45046 /**
45047  * @class Roo.BorderLayout
45048  * @extends Roo.LayoutManager
45049  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
45050  * please see: <br><br>
45051  * <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>
45052  * <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>
45053  * Example:
45054  <pre><code>
45055  var layout = new Roo.BorderLayout(document.body, {
45056     north: {
45057         initialSize: 25,
45058         titlebar: false
45059     },
45060     west: {
45061         split:true,
45062         initialSize: 200,
45063         minSize: 175,
45064         maxSize: 400,
45065         titlebar: true,
45066         collapsible: true
45067     },
45068     east: {
45069         split:true,
45070         initialSize: 202,
45071         minSize: 175,
45072         maxSize: 400,
45073         titlebar: true,
45074         collapsible: true
45075     },
45076     south: {
45077         split:true,
45078         initialSize: 100,
45079         minSize: 100,
45080         maxSize: 200,
45081         titlebar: true,
45082         collapsible: true
45083     },
45084     center: {
45085         titlebar: true,
45086         autoScroll:true,
45087         resizeTabs: true,
45088         minTabWidth: 50,
45089         preferredTabWidth: 150
45090     }
45091 });
45092
45093 // shorthand
45094 var CP = Roo.ContentPanel;
45095
45096 layout.beginUpdate();
45097 layout.add("north", new CP("north", "North"));
45098 layout.add("south", new CP("south", {title: "South", closable: true}));
45099 layout.add("west", new CP("west", {title: "West"}));
45100 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
45101 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
45102 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
45103 layout.getRegion("center").showPanel("center1");
45104 layout.endUpdate();
45105 </code></pre>
45106
45107 <b>The container the layout is rendered into can be either the body element or any other element.
45108 If it is not the body element, the container needs to either be an absolute positioned element,
45109 or you will need to add "position:relative" to the css of the container.  You will also need to specify
45110 the container size if it is not the body element.</b>
45111
45112 * @constructor
45113 * Create a new BorderLayout
45114 * @param {String/HTMLElement/Element} container The container this layout is bound to
45115 * @param {Object} config Configuration options
45116  */
45117 Roo.BorderLayout = function(container, config){
45118     config = config || {};
45119     Roo.BorderLayout.superclass.constructor.call(this, container, config);
45120     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
45121     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
45122         var target = this.factory.validRegions[i];
45123         if(config[target]){
45124             this.addRegion(target, config[target]);
45125         }
45126     }
45127 };
45128
45129 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
45130     /**
45131      * Creates and adds a new region if it doesn't already exist.
45132      * @param {String} target The target region key (north, south, east, west or center).
45133      * @param {Object} config The regions config object
45134      * @return {BorderLayoutRegion} The new region
45135      */
45136     addRegion : function(target, config){
45137         if(!this.regions[target]){
45138             var r = this.factory.create(target, this, config);
45139             this.bindRegion(target, r);
45140         }
45141         return this.regions[target];
45142     },
45143
45144     // private (kinda)
45145     bindRegion : function(name, r){
45146         this.regions[name] = r;
45147         r.on("visibilitychange", this.layout, this);
45148         r.on("paneladded", this.layout, this);
45149         r.on("panelremoved", this.layout, this);
45150         r.on("invalidated", this.layout, this);
45151         r.on("resized", this.onRegionResized, this);
45152         r.on("collapsed", this.onRegionCollapsed, this);
45153         r.on("expanded", this.onRegionExpanded, this);
45154     },
45155
45156     /**
45157      * Performs a layout update.
45158      */
45159     layout : function(){
45160         if(this.updating) return;
45161         var size = this.getViewSize();
45162         var w = size.width;
45163         var h = size.height;
45164         var centerW = w;
45165         var centerH = h;
45166         var centerY = 0;
45167         var centerX = 0;
45168         //var x = 0, y = 0;
45169
45170         var rs = this.regions;
45171         var north = rs["north"];
45172         var south = rs["south"]; 
45173         var west = rs["west"];
45174         var east = rs["east"];
45175         var center = rs["center"];
45176         //if(this.hideOnLayout){ // not supported anymore
45177             //c.el.setStyle("display", "none");
45178         //}
45179         if(north && north.isVisible()){
45180             var b = north.getBox();
45181             var m = north.getMargins();
45182             b.width = w - (m.left+m.right);
45183             b.x = m.left;
45184             b.y = m.top;
45185             centerY = b.height + b.y + m.bottom;
45186             centerH -= centerY;
45187             north.updateBox(this.safeBox(b));
45188         }
45189         if(south && south.isVisible()){
45190             var b = south.getBox();
45191             var m = south.getMargins();
45192             b.width = w - (m.left+m.right);
45193             b.x = m.left;
45194             var totalHeight = (b.height + m.top + m.bottom);
45195             b.y = h - totalHeight + m.top;
45196             centerH -= totalHeight;
45197             south.updateBox(this.safeBox(b));
45198         }
45199         if(west && west.isVisible()){
45200             var b = west.getBox();
45201             var m = west.getMargins();
45202             b.height = centerH - (m.top+m.bottom);
45203             b.x = m.left;
45204             b.y = centerY + m.top;
45205             var totalWidth = (b.width + m.left + m.right);
45206             centerX += totalWidth;
45207             centerW -= totalWidth;
45208             west.updateBox(this.safeBox(b));
45209         }
45210         if(east && east.isVisible()){
45211             var b = east.getBox();
45212             var m = east.getMargins();
45213             b.height = centerH - (m.top+m.bottom);
45214             var totalWidth = (b.width + m.left + m.right);
45215             b.x = w - totalWidth + m.left;
45216             b.y = centerY + m.top;
45217             centerW -= totalWidth;
45218             east.updateBox(this.safeBox(b));
45219         }
45220         if(center){
45221             var m = center.getMargins();
45222             var centerBox = {
45223                 x: centerX + m.left,
45224                 y: centerY + m.top,
45225                 width: centerW - (m.left+m.right),
45226                 height: centerH - (m.top+m.bottom)
45227             };
45228             //if(this.hideOnLayout){
45229                 //center.el.setStyle("display", "block");
45230             //}
45231             center.updateBox(this.safeBox(centerBox));
45232         }
45233         this.el.repaint();
45234         this.fireEvent("layout", this);
45235     },
45236
45237     // private
45238     safeBox : function(box){
45239         box.width = Math.max(0, box.width);
45240         box.height = Math.max(0, box.height);
45241         return box;
45242     },
45243
45244     /**
45245      * Adds a ContentPanel (or subclass) to this layout.
45246      * @param {String} target The target region key (north, south, east, west or center).
45247      * @param {Roo.ContentPanel} panel The panel to add
45248      * @return {Roo.ContentPanel} The added panel
45249      */
45250     add : function(target, panel){
45251          
45252         target = target.toLowerCase();
45253         return this.regions[target].add(panel);
45254     },
45255
45256     /**
45257      * Remove a ContentPanel (or subclass) to this layout.
45258      * @param {String} target The target region key (north, south, east, west or center).
45259      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
45260      * @return {Roo.ContentPanel} The removed panel
45261      */
45262     remove : function(target, panel){
45263         target = target.toLowerCase();
45264         return this.regions[target].remove(panel);
45265     },
45266
45267     /**
45268      * Searches all regions for a panel with the specified id
45269      * @param {String} panelId
45270      * @return {Roo.ContentPanel} The panel or null if it wasn't found
45271      */
45272     findPanel : function(panelId){
45273         var rs = this.regions;
45274         for(var target in rs){
45275             if(typeof rs[target] != "function"){
45276                 var p = rs[target].getPanel(panelId);
45277                 if(p){
45278                     return p;
45279                 }
45280             }
45281         }
45282         return null;
45283     },
45284
45285     /**
45286      * Searches all regions for a panel with the specified id and activates (shows) it.
45287      * @param {String/ContentPanel} panelId The panels id or the panel itself
45288      * @return {Roo.ContentPanel} The shown panel or null
45289      */
45290     showPanel : function(panelId) {
45291       var rs = this.regions;
45292       for(var target in rs){
45293          var r = rs[target];
45294          if(typeof r != "function"){
45295             if(r.hasPanel(panelId)){
45296                return r.showPanel(panelId);
45297             }
45298          }
45299       }
45300       return null;
45301    },
45302
45303    /**
45304      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
45305      * @param {Roo.state.Provider} provider (optional) An alternate state provider
45306      */
45307     restoreState : function(provider){
45308         if(!provider){
45309             provider = Roo.state.Manager;
45310         }
45311         var sm = new Roo.LayoutStateManager();
45312         sm.init(this, provider);
45313     },
45314
45315     /**
45316      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
45317      * object should contain properties for each region to add ContentPanels to, and each property's value should be
45318      * a valid ContentPanel config object.  Example:
45319      * <pre><code>
45320 // Create the main layout
45321 var layout = new Roo.BorderLayout('main-ct', {
45322     west: {
45323         split:true,
45324         minSize: 175,
45325         titlebar: true
45326     },
45327     center: {
45328         title:'Components'
45329     }
45330 }, 'main-ct');
45331
45332 // Create and add multiple ContentPanels at once via configs
45333 layout.batchAdd({
45334    west: {
45335        id: 'source-files',
45336        autoCreate:true,
45337        title:'Ext Source Files',
45338        autoScroll:true,
45339        fitToFrame:true
45340    },
45341    center : {
45342        el: cview,
45343        autoScroll:true,
45344        fitToFrame:true,
45345        toolbar: tb,
45346        resizeEl:'cbody'
45347    }
45348 });
45349 </code></pre>
45350      * @param {Object} regions An object containing ContentPanel configs by region name
45351      */
45352     batchAdd : function(regions){
45353         this.beginUpdate();
45354         for(var rname in regions){
45355             var lr = this.regions[rname];
45356             if(lr){
45357                 this.addTypedPanels(lr, regions[rname]);
45358             }
45359         }
45360         this.endUpdate();
45361     },
45362
45363     // private
45364     addTypedPanels : function(lr, ps){
45365         if(typeof ps == 'string'){
45366             lr.add(new Roo.ContentPanel(ps));
45367         }
45368         else if(ps instanceof Array){
45369             for(var i =0, len = ps.length; i < len; i++){
45370                 this.addTypedPanels(lr, ps[i]);
45371             }
45372         }
45373         else if(!ps.events){ // raw config?
45374             var el = ps.el;
45375             delete ps.el; // prevent conflict
45376             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
45377         }
45378         else {  // panel object assumed!
45379             lr.add(ps);
45380         }
45381     },
45382     /**
45383      * Adds a xtype elements to the layout.
45384      * <pre><code>
45385
45386 layout.addxtype({
45387        xtype : 'ContentPanel',
45388        region: 'west',
45389        items: [ .... ]
45390    }
45391 );
45392
45393 layout.addxtype({
45394         xtype : 'NestedLayoutPanel',
45395         region: 'west',
45396         layout: {
45397            center: { },
45398            west: { }   
45399         },
45400         items : [ ... list of content panels or nested layout panels.. ]
45401    }
45402 );
45403 </code></pre>
45404      * @param {Object} cfg Xtype definition of item to add.
45405      */
45406     addxtype : function(cfg)
45407     {
45408         // basically accepts a pannel...
45409         // can accept a layout region..!?!?
45410         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
45411         
45412         if (!cfg.xtype.match(/Panel$/)) {
45413             return false;
45414         }
45415         var ret = false;
45416         
45417         if (typeof(cfg.region) == 'undefined') {
45418             Roo.log("Failed to add Panel, region was not set");
45419             Roo.log(cfg);
45420             return false;
45421         }
45422         var region = cfg.region;
45423         delete cfg.region;
45424         
45425           
45426         var xitems = [];
45427         if (cfg.items) {
45428             xitems = cfg.items;
45429             delete cfg.items;
45430         }
45431         var nb = false;
45432         
45433         switch(cfg.xtype) 
45434         {
45435             case 'ContentPanel':  // ContentPanel (el, cfg)
45436             case 'ScrollPanel':  // ContentPanel (el, cfg)
45437                 if(cfg.autoCreate) {
45438                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45439                 } else {
45440                     var el = this.el.createChild();
45441                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
45442                 }
45443                 
45444                 this.add(region, ret);
45445                 break;
45446             
45447             
45448             case 'TreePanel': // our new panel!
45449                 cfg.el = this.el.createChild();
45450                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
45451                 this.add(region, ret);
45452                 break;
45453             
45454             case 'NestedLayoutPanel': 
45455                 // create a new Layout (which is  a Border Layout...
45456                 var el = this.el.createChild();
45457                 var clayout = cfg.layout;
45458                 delete cfg.layout;
45459                 clayout.items   = clayout.items  || [];
45460                 // replace this exitems with the clayout ones..
45461                 xitems = clayout.items;
45462                  
45463                 
45464                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
45465                     cfg.background = false;
45466                 }
45467                 var layout = new Roo.BorderLayout(el, clayout);
45468                 
45469                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
45470                 //console.log('adding nested layout panel '  + cfg.toSource());
45471                 this.add(region, ret);
45472                 nb = {}; /// find first...
45473                 break;
45474                 
45475             case 'GridPanel': 
45476             
45477                 // needs grid and region
45478                 
45479                 //var el = this.getRegion(region).el.createChild();
45480                 var el = this.el.createChild();
45481                 // create the grid first...
45482                 
45483                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
45484                 delete cfg.grid;
45485                 if (region == 'center' && this.active ) {
45486                     cfg.background = false;
45487                 }
45488                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
45489                 
45490                 this.add(region, ret);
45491                 if (cfg.background) {
45492                     ret.on('activate', function(gp) {
45493                         if (!gp.grid.rendered) {
45494                             gp.grid.render();
45495                         }
45496                     });
45497                 } else {
45498                     grid.render();
45499                 }
45500                 break;
45501            
45502                
45503                 
45504                 
45505             default: 
45506                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
45507                 return null;
45508              // GridPanel (grid, cfg)
45509             
45510         }
45511         this.beginUpdate();
45512         // add children..
45513         var region = '';
45514         var abn = {};
45515         Roo.each(xitems, function(i)  {
45516             region = nb && i.region ? i.region : false;
45517             
45518             var add = ret.addxtype(i);
45519            
45520             if (region) {
45521                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
45522                 if (!i.background) {
45523                     abn[region] = nb[region] ;
45524                 }
45525             }
45526             
45527         });
45528         this.endUpdate();
45529
45530         // make the last non-background panel active..
45531         //if (nb) { Roo.log(abn); }
45532         if (nb) {
45533             
45534             for(var r in abn) {
45535                 region = this.getRegion(r);
45536                 if (region) {
45537                     // tried using nb[r], but it does not work..
45538                      
45539                     region.showPanel(abn[r]);
45540                    
45541                 }
45542             }
45543         }
45544         return ret;
45545         
45546     }
45547 });
45548
45549 /**
45550  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
45551  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
45552  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
45553  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
45554  * <pre><code>
45555 // shorthand
45556 var CP = Roo.ContentPanel;
45557
45558 var layout = Roo.BorderLayout.create({
45559     north: {
45560         initialSize: 25,
45561         titlebar: false,
45562         panels: [new CP("north", "North")]
45563     },
45564     west: {
45565         split:true,
45566         initialSize: 200,
45567         minSize: 175,
45568         maxSize: 400,
45569         titlebar: true,
45570         collapsible: true,
45571         panels: [new CP("west", {title: "West"})]
45572     },
45573     east: {
45574         split:true,
45575         initialSize: 202,
45576         minSize: 175,
45577         maxSize: 400,
45578         titlebar: true,
45579         collapsible: true,
45580         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45581     },
45582     south: {
45583         split:true,
45584         initialSize: 100,
45585         minSize: 100,
45586         maxSize: 200,
45587         titlebar: true,
45588         collapsible: true,
45589         panels: [new CP("south", {title: "South", closable: true})]
45590     },
45591     center: {
45592         titlebar: true,
45593         autoScroll:true,
45594         resizeTabs: true,
45595         minTabWidth: 50,
45596         preferredTabWidth: 150,
45597         panels: [
45598             new CP("center1", {title: "Close Me", closable: true}),
45599             new CP("center2", {title: "Center Panel", closable: false})
45600         ]
45601     }
45602 }, document.body);
45603
45604 layout.getRegion("center").showPanel("center1");
45605 </code></pre>
45606  * @param config
45607  * @param targetEl
45608  */
45609 Roo.BorderLayout.create = function(config, targetEl){
45610     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45611     layout.beginUpdate();
45612     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45613     for(var j = 0, jlen = regions.length; j < jlen; j++){
45614         var lr = regions[j];
45615         if(layout.regions[lr] && config[lr].panels){
45616             var r = layout.regions[lr];
45617             var ps = config[lr].panels;
45618             layout.addTypedPanels(r, ps);
45619         }
45620     }
45621     layout.endUpdate();
45622     return layout;
45623 };
45624
45625 // private
45626 Roo.BorderLayout.RegionFactory = {
45627     // private
45628     validRegions : ["north","south","east","west","center"],
45629
45630     // private
45631     create : function(target, mgr, config){
45632         target = target.toLowerCase();
45633         if(config.lightweight || config.basic){
45634             return new Roo.BasicLayoutRegion(mgr, config, target);
45635         }
45636         switch(target){
45637             case "north":
45638                 return new Roo.NorthLayoutRegion(mgr, config);
45639             case "south":
45640                 return new Roo.SouthLayoutRegion(mgr, config);
45641             case "east":
45642                 return new Roo.EastLayoutRegion(mgr, config);
45643             case "west":
45644                 return new Roo.WestLayoutRegion(mgr, config);
45645             case "center":
45646                 return new Roo.CenterLayoutRegion(mgr, config);
45647         }
45648         throw 'Layout region "'+target+'" not supported.';
45649     }
45650 };/*
45651  * Based on:
45652  * Ext JS Library 1.1.1
45653  * Copyright(c) 2006-2007, Ext JS, LLC.
45654  *
45655  * Originally Released Under LGPL - original licence link has changed is not relivant.
45656  *
45657  * Fork - LGPL
45658  * <script type="text/javascript">
45659  */
45660  
45661 /**
45662  * @class Roo.BasicLayoutRegion
45663  * @extends Roo.util.Observable
45664  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45665  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45666  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45667  */
45668 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45669     this.mgr = mgr;
45670     this.position  = pos;
45671     this.events = {
45672         /**
45673          * @scope Roo.BasicLayoutRegion
45674          */
45675         
45676         /**
45677          * @event beforeremove
45678          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45679          * @param {Roo.LayoutRegion} this
45680          * @param {Roo.ContentPanel} panel The panel
45681          * @param {Object} e The cancel event object
45682          */
45683         "beforeremove" : true,
45684         /**
45685          * @event invalidated
45686          * Fires when the layout for this region is changed.
45687          * @param {Roo.LayoutRegion} this
45688          */
45689         "invalidated" : true,
45690         /**
45691          * @event visibilitychange
45692          * Fires when this region is shown or hidden 
45693          * @param {Roo.LayoutRegion} this
45694          * @param {Boolean} visibility true or false
45695          */
45696         "visibilitychange" : true,
45697         /**
45698          * @event paneladded
45699          * Fires when a panel is added. 
45700          * @param {Roo.LayoutRegion} this
45701          * @param {Roo.ContentPanel} panel The panel
45702          */
45703         "paneladded" : true,
45704         /**
45705          * @event panelremoved
45706          * Fires when a panel is removed. 
45707          * @param {Roo.LayoutRegion} this
45708          * @param {Roo.ContentPanel} panel The panel
45709          */
45710         "panelremoved" : true,
45711         /**
45712          * @event collapsed
45713          * Fires when this region is collapsed.
45714          * @param {Roo.LayoutRegion} this
45715          */
45716         "collapsed" : true,
45717         /**
45718          * @event expanded
45719          * Fires when this region is expanded.
45720          * @param {Roo.LayoutRegion} this
45721          */
45722         "expanded" : true,
45723         /**
45724          * @event slideshow
45725          * Fires when this region is slid into view.
45726          * @param {Roo.LayoutRegion} this
45727          */
45728         "slideshow" : true,
45729         /**
45730          * @event slidehide
45731          * Fires when this region slides out of view. 
45732          * @param {Roo.LayoutRegion} this
45733          */
45734         "slidehide" : true,
45735         /**
45736          * @event panelactivated
45737          * Fires when a panel is activated. 
45738          * @param {Roo.LayoutRegion} this
45739          * @param {Roo.ContentPanel} panel The activated panel
45740          */
45741         "panelactivated" : true,
45742         /**
45743          * @event resized
45744          * Fires when the user resizes this region. 
45745          * @param {Roo.LayoutRegion} this
45746          * @param {Number} newSize The new size (width for east/west, height for north/south)
45747          */
45748         "resized" : true
45749     };
45750     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45751     this.panels = new Roo.util.MixedCollection();
45752     this.panels.getKey = this.getPanelId.createDelegate(this);
45753     this.box = null;
45754     this.activePanel = null;
45755     // ensure listeners are added...
45756     
45757     if (config.listeners || config.events) {
45758         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45759             listeners : config.listeners || {},
45760             events : config.events || {}
45761         });
45762     }
45763     
45764     if(skipConfig !== true){
45765         this.applyConfig(config);
45766     }
45767 };
45768
45769 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45770     getPanelId : function(p){
45771         return p.getId();
45772     },
45773     
45774     applyConfig : function(config){
45775         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45776         this.config = config;
45777         
45778     },
45779     
45780     /**
45781      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45782      * the width, for horizontal (north, south) the height.
45783      * @param {Number} newSize The new width or height
45784      */
45785     resizeTo : function(newSize){
45786         var el = this.el ? this.el :
45787                  (this.activePanel ? this.activePanel.getEl() : null);
45788         if(el){
45789             switch(this.position){
45790                 case "east":
45791                 case "west":
45792                     el.setWidth(newSize);
45793                     this.fireEvent("resized", this, newSize);
45794                 break;
45795                 case "north":
45796                 case "south":
45797                     el.setHeight(newSize);
45798                     this.fireEvent("resized", this, newSize);
45799                 break;                
45800             }
45801         }
45802     },
45803     
45804     getBox : function(){
45805         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45806     },
45807     
45808     getMargins : function(){
45809         return this.margins;
45810     },
45811     
45812     updateBox : function(box){
45813         this.box = box;
45814         var el = this.activePanel.getEl();
45815         el.dom.style.left = box.x + "px";
45816         el.dom.style.top = box.y + "px";
45817         this.activePanel.setSize(box.width, box.height);
45818     },
45819     
45820     /**
45821      * Returns the container element for this region.
45822      * @return {Roo.Element}
45823      */
45824     getEl : function(){
45825         return this.activePanel;
45826     },
45827     
45828     /**
45829      * Returns true if this region is currently visible.
45830      * @return {Boolean}
45831      */
45832     isVisible : function(){
45833         return this.activePanel ? true : false;
45834     },
45835     
45836     setActivePanel : function(panel){
45837         panel = this.getPanel(panel);
45838         if(this.activePanel && this.activePanel != panel){
45839             this.activePanel.setActiveState(false);
45840             this.activePanel.getEl().setLeftTop(-10000,-10000);
45841         }
45842         this.activePanel = panel;
45843         panel.setActiveState(true);
45844         if(this.box){
45845             panel.setSize(this.box.width, this.box.height);
45846         }
45847         this.fireEvent("panelactivated", this, panel);
45848         this.fireEvent("invalidated");
45849     },
45850     
45851     /**
45852      * Show the specified panel.
45853      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45854      * @return {Roo.ContentPanel} The shown panel or null
45855      */
45856     showPanel : function(panel){
45857         if(panel = this.getPanel(panel)){
45858             this.setActivePanel(panel);
45859         }
45860         return panel;
45861     },
45862     
45863     /**
45864      * Get the active panel for this region.
45865      * @return {Roo.ContentPanel} The active panel or null
45866      */
45867     getActivePanel : function(){
45868         return this.activePanel;
45869     },
45870     
45871     /**
45872      * Add the passed ContentPanel(s)
45873      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45874      * @return {Roo.ContentPanel} The panel added (if only one was added)
45875      */
45876     add : function(panel){
45877         if(arguments.length > 1){
45878             for(var i = 0, len = arguments.length; i < len; i++) {
45879                 this.add(arguments[i]);
45880             }
45881             return null;
45882         }
45883         if(this.hasPanel(panel)){
45884             this.showPanel(panel);
45885             return panel;
45886         }
45887         var el = panel.getEl();
45888         if(el.dom.parentNode != this.mgr.el.dom){
45889             this.mgr.el.dom.appendChild(el.dom);
45890         }
45891         if(panel.setRegion){
45892             panel.setRegion(this);
45893         }
45894         this.panels.add(panel);
45895         el.setStyle("position", "absolute");
45896         if(!panel.background){
45897             this.setActivePanel(panel);
45898             if(this.config.initialSize && this.panels.getCount()==1){
45899                 this.resizeTo(this.config.initialSize);
45900             }
45901         }
45902         this.fireEvent("paneladded", this, panel);
45903         return panel;
45904     },
45905     
45906     /**
45907      * Returns true if the panel is in this region.
45908      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45909      * @return {Boolean}
45910      */
45911     hasPanel : function(panel){
45912         if(typeof panel == "object"){ // must be panel obj
45913             panel = panel.getId();
45914         }
45915         return this.getPanel(panel) ? true : false;
45916     },
45917     
45918     /**
45919      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45920      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45921      * @param {Boolean} preservePanel Overrides the config preservePanel option
45922      * @return {Roo.ContentPanel} The panel that was removed
45923      */
45924     remove : function(panel, preservePanel){
45925         panel = this.getPanel(panel);
45926         if(!panel){
45927             return null;
45928         }
45929         var e = {};
45930         this.fireEvent("beforeremove", this, panel, e);
45931         if(e.cancel === true){
45932             return null;
45933         }
45934         var panelId = panel.getId();
45935         this.panels.removeKey(panelId);
45936         return panel;
45937     },
45938     
45939     /**
45940      * Returns the panel specified or null if it's not in this region.
45941      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45942      * @return {Roo.ContentPanel}
45943      */
45944     getPanel : function(id){
45945         if(typeof id == "object"){ // must be panel obj
45946             return id;
45947         }
45948         return this.panels.get(id);
45949     },
45950     
45951     /**
45952      * Returns this regions position (north/south/east/west/center).
45953      * @return {String} 
45954      */
45955     getPosition: function(){
45956         return this.position;    
45957     }
45958 });/*
45959  * Based on:
45960  * Ext JS Library 1.1.1
45961  * Copyright(c) 2006-2007, Ext JS, LLC.
45962  *
45963  * Originally Released Under LGPL - original licence link has changed is not relivant.
45964  *
45965  * Fork - LGPL
45966  * <script type="text/javascript">
45967  */
45968  
45969 /**
45970  * @class Roo.LayoutRegion
45971  * @extends Roo.BasicLayoutRegion
45972  * This class represents a region in a layout manager.
45973  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45974  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45975  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45976  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45977  * @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})
45978  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45979  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45980  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45981  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45982  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45983  * @cfg {String}    title           The title for the region (overrides panel titles)
45984  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45985  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45986  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45987  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45988  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45989  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45990  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45991  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45992  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45993  * @cfg {Boolean}   showPin         True to show a pin button
45994  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45995  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45996  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45997  * @cfg {Number}    width           For East/West panels
45998  * @cfg {Number}    height          For North/South panels
45999  * @cfg {Boolean}   split           To show the splitter
46000  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
46001  */
46002 Roo.LayoutRegion = function(mgr, config, pos){
46003     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
46004     var dh = Roo.DomHelper;
46005     /** This region's container element 
46006     * @type Roo.Element */
46007     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
46008     /** This region's title element 
46009     * @type Roo.Element */
46010
46011     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
46012         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
46013         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
46014     ]}, true);
46015     this.titleEl.enableDisplayMode();
46016     /** This region's title text element 
46017     * @type HTMLElement */
46018     this.titleTextEl = this.titleEl.dom.firstChild;
46019     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
46020     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
46021     this.closeBtn.enableDisplayMode();
46022     this.closeBtn.on("click", this.closeClicked, this);
46023     this.closeBtn.hide();
46024
46025     this.createBody(config);
46026     this.visible = true;
46027     this.collapsed = false;
46028
46029     if(config.hideWhenEmpty){
46030         this.hide();
46031         this.on("paneladded", this.validateVisibility, this);
46032         this.on("panelremoved", this.validateVisibility, this);
46033     }
46034     this.applyConfig(config);
46035 };
46036
46037 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
46038
46039     createBody : function(){
46040         /** This region's body element 
46041         * @type Roo.Element */
46042         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
46043     },
46044
46045     applyConfig : function(c){
46046         if(c.collapsible && this.position != "center" && !this.collapsedEl){
46047             var dh = Roo.DomHelper;
46048             if(c.titlebar !== false){
46049                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
46050                 this.collapseBtn.on("click", this.collapse, this);
46051                 this.collapseBtn.enableDisplayMode();
46052
46053                 if(c.showPin === true || this.showPin){
46054                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
46055                     this.stickBtn.enableDisplayMode();
46056                     this.stickBtn.on("click", this.expand, this);
46057                     this.stickBtn.hide();
46058                 }
46059             }
46060             /** This region's collapsed element
46061             * @type Roo.Element */
46062             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
46063                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
46064             ]}, true);
46065             if(c.floatable !== false){
46066                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
46067                this.collapsedEl.on("click", this.collapseClick, this);
46068             }
46069
46070             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
46071                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
46072                    id: "message", unselectable: "on", style:{"float":"left"}});
46073                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
46074              }
46075             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
46076             this.expandBtn.on("click", this.expand, this);
46077         }
46078         if(this.collapseBtn){
46079             this.collapseBtn.setVisible(c.collapsible == true);
46080         }
46081         this.cmargins = c.cmargins || this.cmargins ||
46082                          (this.position == "west" || this.position == "east" ?
46083                              {top: 0, left: 2, right:2, bottom: 0} :
46084                              {top: 2, left: 0, right:0, bottom: 2});
46085         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
46086         this.bottomTabs = c.tabPosition != "top";
46087         this.autoScroll = c.autoScroll || false;
46088         if(this.autoScroll){
46089             this.bodyEl.setStyle("overflow", "auto");
46090         }else{
46091             this.bodyEl.setStyle("overflow", "hidden");
46092         }
46093         //if(c.titlebar !== false){
46094             if((!c.titlebar && !c.title) || c.titlebar === false){
46095                 this.titleEl.hide();
46096             }else{
46097                 this.titleEl.show();
46098                 if(c.title){
46099                     this.titleTextEl.innerHTML = c.title;
46100                 }
46101             }
46102         //}
46103         this.duration = c.duration || .30;
46104         this.slideDuration = c.slideDuration || .45;
46105         this.config = c;
46106         if(c.collapsed){
46107             this.collapse(true);
46108         }
46109         if(c.hidden){
46110             this.hide();
46111         }
46112     },
46113     /**
46114      * Returns true if this region is currently visible.
46115      * @return {Boolean}
46116      */
46117     isVisible : function(){
46118         return this.visible;
46119     },
46120
46121     /**
46122      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
46123      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
46124      */
46125     setCollapsedTitle : function(title){
46126         title = title || "&#160;";
46127         if(this.collapsedTitleTextEl){
46128             this.collapsedTitleTextEl.innerHTML = title;
46129         }
46130     },
46131
46132     getBox : function(){
46133         var b;
46134         if(!this.collapsed){
46135             b = this.el.getBox(false, true);
46136         }else{
46137             b = this.collapsedEl.getBox(false, true);
46138         }
46139         return b;
46140     },
46141
46142     getMargins : function(){
46143         return this.collapsed ? this.cmargins : this.margins;
46144     },
46145
46146     highlight : function(){
46147         this.el.addClass("x-layout-panel-dragover");
46148     },
46149
46150     unhighlight : function(){
46151         this.el.removeClass("x-layout-panel-dragover");
46152     },
46153
46154     updateBox : function(box){
46155         this.box = box;
46156         if(!this.collapsed){
46157             this.el.dom.style.left = box.x + "px";
46158             this.el.dom.style.top = box.y + "px";
46159             this.updateBody(box.width, box.height);
46160         }else{
46161             this.collapsedEl.dom.style.left = box.x + "px";
46162             this.collapsedEl.dom.style.top = box.y + "px";
46163             this.collapsedEl.setSize(box.width, box.height);
46164         }
46165         if(this.tabs){
46166             this.tabs.autoSizeTabs();
46167         }
46168     },
46169
46170     updateBody : function(w, h){
46171         if(w !== null){
46172             this.el.setWidth(w);
46173             w -= this.el.getBorderWidth("rl");
46174             if(this.config.adjustments){
46175                 w += this.config.adjustments[0];
46176             }
46177         }
46178         if(h !== null){
46179             this.el.setHeight(h);
46180             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
46181             h -= this.el.getBorderWidth("tb");
46182             if(this.config.adjustments){
46183                 h += this.config.adjustments[1];
46184             }
46185             this.bodyEl.setHeight(h);
46186             if(this.tabs){
46187                 h = this.tabs.syncHeight(h);
46188             }
46189         }
46190         if(this.panelSize){
46191             w = w !== null ? w : this.panelSize.width;
46192             h = h !== null ? h : this.panelSize.height;
46193         }
46194         if(this.activePanel){
46195             var el = this.activePanel.getEl();
46196             w = w !== null ? w : el.getWidth();
46197             h = h !== null ? h : el.getHeight();
46198             this.panelSize = {width: w, height: h};
46199             this.activePanel.setSize(w, h);
46200         }
46201         if(Roo.isIE && this.tabs){
46202             this.tabs.el.repaint();
46203         }
46204     },
46205
46206     /**
46207      * Returns the container element for this region.
46208      * @return {Roo.Element}
46209      */
46210     getEl : function(){
46211         return this.el;
46212     },
46213
46214     /**
46215      * Hides this region.
46216      */
46217     hide : function(){
46218         if(!this.collapsed){
46219             this.el.dom.style.left = "-2000px";
46220             this.el.hide();
46221         }else{
46222             this.collapsedEl.dom.style.left = "-2000px";
46223             this.collapsedEl.hide();
46224         }
46225         this.visible = false;
46226         this.fireEvent("visibilitychange", this, false);
46227     },
46228
46229     /**
46230      * Shows this region if it was previously hidden.
46231      */
46232     show : function(){
46233         if(!this.collapsed){
46234             this.el.show();
46235         }else{
46236             this.collapsedEl.show();
46237         }
46238         this.visible = true;
46239         this.fireEvent("visibilitychange", this, true);
46240     },
46241
46242     closeClicked : function(){
46243         if(this.activePanel){
46244             this.remove(this.activePanel);
46245         }
46246     },
46247
46248     collapseClick : function(e){
46249         if(this.isSlid){
46250            e.stopPropagation();
46251            this.slideIn();
46252         }else{
46253            e.stopPropagation();
46254            this.slideOut();
46255         }
46256     },
46257
46258     /**
46259      * Collapses this region.
46260      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
46261      */
46262     collapse : function(skipAnim){
46263         if(this.collapsed) return;
46264         this.collapsed = true;
46265         if(this.split){
46266             this.split.el.hide();
46267         }
46268         if(this.config.animate && skipAnim !== true){
46269             this.fireEvent("invalidated", this);
46270             this.animateCollapse();
46271         }else{
46272             this.el.setLocation(-20000,-20000);
46273             this.el.hide();
46274             this.collapsedEl.show();
46275             this.fireEvent("collapsed", this);
46276             this.fireEvent("invalidated", this);
46277         }
46278     },
46279
46280     animateCollapse : function(){
46281         // overridden
46282     },
46283
46284     /**
46285      * Expands this region if it was previously collapsed.
46286      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
46287      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
46288      */
46289     expand : function(e, skipAnim){
46290         if(e) e.stopPropagation();
46291         if(!this.collapsed || this.el.hasActiveFx()) return;
46292         if(this.isSlid){
46293             this.afterSlideIn();
46294             skipAnim = true;
46295         }
46296         this.collapsed = false;
46297         if(this.config.animate && skipAnim !== true){
46298             this.animateExpand();
46299         }else{
46300             this.el.show();
46301             if(this.split){
46302                 this.split.el.show();
46303             }
46304             this.collapsedEl.setLocation(-2000,-2000);
46305             this.collapsedEl.hide();
46306             this.fireEvent("invalidated", this);
46307             this.fireEvent("expanded", this);
46308         }
46309     },
46310
46311     animateExpand : function(){
46312         // overridden
46313     },
46314
46315     initTabs : function()
46316     {
46317         this.bodyEl.setStyle("overflow", "hidden");
46318         var ts = new Roo.TabPanel(
46319                 this.bodyEl.dom,
46320                 {
46321                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
46322                     disableTooltips: this.config.disableTabTips,
46323                     toolbar : this.config.toolbar
46324                 }
46325         );
46326         if(this.config.hideTabs){
46327             ts.stripWrap.setDisplayed(false);
46328         }
46329         this.tabs = ts;
46330         ts.resizeTabs = this.config.resizeTabs === true;
46331         ts.minTabWidth = this.config.minTabWidth || 40;
46332         ts.maxTabWidth = this.config.maxTabWidth || 250;
46333         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
46334         ts.monitorResize = false;
46335         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46336         ts.bodyEl.addClass('x-layout-tabs-body');
46337         this.panels.each(this.initPanelAsTab, this);
46338     },
46339
46340     initPanelAsTab : function(panel){
46341         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
46342                     this.config.closeOnTab && panel.isClosable());
46343         if(panel.tabTip !== undefined){
46344             ti.setTooltip(panel.tabTip);
46345         }
46346         ti.on("activate", function(){
46347               this.setActivePanel(panel);
46348         }, this);
46349         if(this.config.closeOnTab){
46350             ti.on("beforeclose", function(t, e){
46351                 e.cancel = true;
46352                 this.remove(panel);
46353             }, this);
46354         }
46355         return ti;
46356     },
46357
46358     updatePanelTitle : function(panel, title){
46359         if(this.activePanel == panel){
46360             this.updateTitle(title);
46361         }
46362         if(this.tabs){
46363             var ti = this.tabs.getTab(panel.getEl().id);
46364             ti.setText(title);
46365             if(panel.tabTip !== undefined){
46366                 ti.setTooltip(panel.tabTip);
46367             }
46368         }
46369     },
46370
46371     updateTitle : function(title){
46372         if(this.titleTextEl && !this.config.title){
46373             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
46374         }
46375     },
46376
46377     setActivePanel : function(panel){
46378         panel = this.getPanel(panel);
46379         if(this.activePanel && this.activePanel != panel){
46380             this.activePanel.setActiveState(false);
46381         }
46382         this.activePanel = panel;
46383         panel.setActiveState(true);
46384         if(this.panelSize){
46385             panel.setSize(this.panelSize.width, this.panelSize.height);
46386         }
46387         if(this.closeBtn){
46388             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
46389         }
46390         this.updateTitle(panel.getTitle());
46391         if(this.tabs){
46392             this.fireEvent("invalidated", this);
46393         }
46394         this.fireEvent("panelactivated", this, panel);
46395     },
46396
46397     /**
46398      * Shows the specified panel.
46399      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
46400      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
46401      */
46402     showPanel : function(panel){
46403         if(panel = this.getPanel(panel)){
46404             if(this.tabs){
46405                 var tab = this.tabs.getTab(panel.getEl().id);
46406                 if(tab.isHidden()){
46407                     this.tabs.unhideTab(tab.id);
46408                 }
46409                 tab.activate();
46410             }else{
46411                 this.setActivePanel(panel);
46412             }
46413         }
46414         return panel;
46415     },
46416
46417     /**
46418      * Get the active panel for this region.
46419      * @return {Roo.ContentPanel} The active panel or null
46420      */
46421     getActivePanel : function(){
46422         return this.activePanel;
46423     },
46424
46425     validateVisibility : function(){
46426         if(this.panels.getCount() < 1){
46427             this.updateTitle("&#160;");
46428             this.closeBtn.hide();
46429             this.hide();
46430         }else{
46431             if(!this.isVisible()){
46432                 this.show();
46433             }
46434         }
46435     },
46436
46437     /**
46438      * Adds the passed ContentPanel(s) to this region.
46439      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
46440      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
46441      */
46442     add : function(panel){
46443         if(arguments.length > 1){
46444             for(var i = 0, len = arguments.length; i < len; i++) {
46445                 this.add(arguments[i]);
46446             }
46447             return null;
46448         }
46449         if(this.hasPanel(panel)){
46450             this.showPanel(panel);
46451             return panel;
46452         }
46453         panel.setRegion(this);
46454         this.panels.add(panel);
46455         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
46456             this.bodyEl.dom.appendChild(panel.getEl().dom);
46457             if(panel.background !== true){
46458                 this.setActivePanel(panel);
46459             }
46460             this.fireEvent("paneladded", this, panel);
46461             return panel;
46462         }
46463         if(!this.tabs){
46464             this.initTabs();
46465         }else{
46466             this.initPanelAsTab(panel);
46467         }
46468         if(panel.background !== true){
46469             this.tabs.activate(panel.getEl().id);
46470         }
46471         this.fireEvent("paneladded", this, panel);
46472         return panel;
46473     },
46474
46475     /**
46476      * Hides the tab for the specified panel.
46477      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46478      */
46479     hidePanel : function(panel){
46480         if(this.tabs && (panel = this.getPanel(panel))){
46481             this.tabs.hideTab(panel.getEl().id);
46482         }
46483     },
46484
46485     /**
46486      * Unhides the tab for a previously hidden panel.
46487      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46488      */
46489     unhidePanel : function(panel){
46490         if(this.tabs && (panel = this.getPanel(panel))){
46491             this.tabs.unhideTab(panel.getEl().id);
46492         }
46493     },
46494
46495     clearPanels : function(){
46496         while(this.panels.getCount() > 0){
46497              this.remove(this.panels.first());
46498         }
46499     },
46500
46501     /**
46502      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
46503      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
46504      * @param {Boolean} preservePanel Overrides the config preservePanel option
46505      * @return {Roo.ContentPanel} The panel that was removed
46506      */
46507     remove : function(panel, preservePanel){
46508         panel = this.getPanel(panel);
46509         if(!panel){
46510             return null;
46511         }
46512         var e = {};
46513         this.fireEvent("beforeremove", this, panel, e);
46514         if(e.cancel === true){
46515             return null;
46516         }
46517         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
46518         var panelId = panel.getId();
46519         this.panels.removeKey(panelId);
46520         if(preservePanel){
46521             document.body.appendChild(panel.getEl().dom);
46522         }
46523         if(this.tabs){
46524             this.tabs.removeTab(panel.getEl().id);
46525         }else if (!preservePanel){
46526             this.bodyEl.dom.removeChild(panel.getEl().dom);
46527         }
46528         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
46529             var p = this.panels.first();
46530             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
46531             tempEl.appendChild(p.getEl().dom);
46532             this.bodyEl.update("");
46533             this.bodyEl.dom.appendChild(p.getEl().dom);
46534             tempEl = null;
46535             this.updateTitle(p.getTitle());
46536             this.tabs = null;
46537             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
46538             this.setActivePanel(p);
46539         }
46540         panel.setRegion(null);
46541         if(this.activePanel == panel){
46542             this.activePanel = null;
46543         }
46544         if(this.config.autoDestroy !== false && preservePanel !== true){
46545             try{panel.destroy();}catch(e){}
46546         }
46547         this.fireEvent("panelremoved", this, panel);
46548         return panel;
46549     },
46550
46551     /**
46552      * Returns the TabPanel component used by this region
46553      * @return {Roo.TabPanel}
46554      */
46555     getTabs : function(){
46556         return this.tabs;
46557     },
46558
46559     createTool : function(parentEl, className){
46560         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
46561             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
46562         btn.addClassOnOver("x-layout-tools-button-over");
46563         return btn;
46564     }
46565 });/*
46566  * Based on:
46567  * Ext JS Library 1.1.1
46568  * Copyright(c) 2006-2007, Ext JS, LLC.
46569  *
46570  * Originally Released Under LGPL - original licence link has changed is not relivant.
46571  *
46572  * Fork - LGPL
46573  * <script type="text/javascript">
46574  */
46575  
46576
46577
46578 /**
46579  * @class Roo.SplitLayoutRegion
46580  * @extends Roo.LayoutRegion
46581  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46582  */
46583 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46584     this.cursor = cursor;
46585     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46586 };
46587
46588 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46589     splitTip : "Drag to resize.",
46590     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46591     useSplitTips : false,
46592
46593     applyConfig : function(config){
46594         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46595         if(config.split){
46596             if(!this.split){
46597                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46598                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46599                 /** The SplitBar for this region 
46600                 * @type Roo.SplitBar */
46601                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46602                 this.split.on("moved", this.onSplitMove, this);
46603                 this.split.useShim = config.useShim === true;
46604                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46605                 if(this.useSplitTips){
46606                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46607                 }
46608                 if(config.collapsible){
46609                     this.split.el.on("dblclick", this.collapse,  this);
46610                 }
46611             }
46612             if(typeof config.minSize != "undefined"){
46613                 this.split.minSize = config.minSize;
46614             }
46615             if(typeof config.maxSize != "undefined"){
46616                 this.split.maxSize = config.maxSize;
46617             }
46618             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46619                 this.hideSplitter();
46620             }
46621         }
46622     },
46623
46624     getHMaxSize : function(){
46625          var cmax = this.config.maxSize || 10000;
46626          var center = this.mgr.getRegion("center");
46627          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46628     },
46629
46630     getVMaxSize : function(){
46631          var cmax = this.config.maxSize || 10000;
46632          var center = this.mgr.getRegion("center");
46633          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46634     },
46635
46636     onSplitMove : function(split, newSize){
46637         this.fireEvent("resized", this, newSize);
46638     },
46639     
46640     /** 
46641      * Returns the {@link Roo.SplitBar} for this region.
46642      * @return {Roo.SplitBar}
46643      */
46644     getSplitBar : function(){
46645         return this.split;
46646     },
46647     
46648     hide : function(){
46649         this.hideSplitter();
46650         Roo.SplitLayoutRegion.superclass.hide.call(this);
46651     },
46652
46653     hideSplitter : function(){
46654         if(this.split){
46655             this.split.el.setLocation(-2000,-2000);
46656             this.split.el.hide();
46657         }
46658     },
46659
46660     show : function(){
46661         if(this.split){
46662             this.split.el.show();
46663         }
46664         Roo.SplitLayoutRegion.superclass.show.call(this);
46665     },
46666     
46667     beforeSlide: function(){
46668         if(Roo.isGecko){// firefox overflow auto bug workaround
46669             this.bodyEl.clip();
46670             if(this.tabs) this.tabs.bodyEl.clip();
46671             if(this.activePanel){
46672                 this.activePanel.getEl().clip();
46673                 
46674                 if(this.activePanel.beforeSlide){
46675                     this.activePanel.beforeSlide();
46676                 }
46677             }
46678         }
46679     },
46680     
46681     afterSlide : function(){
46682         if(Roo.isGecko){// firefox overflow auto bug workaround
46683             this.bodyEl.unclip();
46684             if(this.tabs) this.tabs.bodyEl.unclip();
46685             if(this.activePanel){
46686                 this.activePanel.getEl().unclip();
46687                 if(this.activePanel.afterSlide){
46688                     this.activePanel.afterSlide();
46689                 }
46690             }
46691         }
46692     },
46693
46694     initAutoHide : function(){
46695         if(this.autoHide !== false){
46696             if(!this.autoHideHd){
46697                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46698                 this.autoHideHd = {
46699                     "mouseout": function(e){
46700                         if(!e.within(this.el, true)){
46701                             st.delay(500);
46702                         }
46703                     },
46704                     "mouseover" : function(e){
46705                         st.cancel();
46706                     },
46707                     scope : this
46708                 };
46709             }
46710             this.el.on(this.autoHideHd);
46711         }
46712     },
46713
46714     clearAutoHide : function(){
46715         if(this.autoHide !== false){
46716             this.el.un("mouseout", this.autoHideHd.mouseout);
46717             this.el.un("mouseover", this.autoHideHd.mouseover);
46718         }
46719     },
46720
46721     clearMonitor : function(){
46722         Roo.get(document).un("click", this.slideInIf, this);
46723     },
46724
46725     // these names are backwards but not changed for compat
46726     slideOut : function(){
46727         if(this.isSlid || this.el.hasActiveFx()){
46728             return;
46729         }
46730         this.isSlid = true;
46731         if(this.collapseBtn){
46732             this.collapseBtn.hide();
46733         }
46734         this.closeBtnState = this.closeBtn.getStyle('display');
46735         this.closeBtn.hide();
46736         if(this.stickBtn){
46737             this.stickBtn.show();
46738         }
46739         this.el.show();
46740         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46741         this.beforeSlide();
46742         this.el.setStyle("z-index", 10001);
46743         this.el.slideIn(this.getSlideAnchor(), {
46744             callback: function(){
46745                 this.afterSlide();
46746                 this.initAutoHide();
46747                 Roo.get(document).on("click", this.slideInIf, this);
46748                 this.fireEvent("slideshow", this);
46749             },
46750             scope: this,
46751             block: true
46752         });
46753     },
46754
46755     afterSlideIn : function(){
46756         this.clearAutoHide();
46757         this.isSlid = false;
46758         this.clearMonitor();
46759         this.el.setStyle("z-index", "");
46760         if(this.collapseBtn){
46761             this.collapseBtn.show();
46762         }
46763         this.closeBtn.setStyle('display', this.closeBtnState);
46764         if(this.stickBtn){
46765             this.stickBtn.hide();
46766         }
46767         this.fireEvent("slidehide", this);
46768     },
46769
46770     slideIn : function(cb){
46771         if(!this.isSlid || this.el.hasActiveFx()){
46772             Roo.callback(cb);
46773             return;
46774         }
46775         this.isSlid = false;
46776         this.beforeSlide();
46777         this.el.slideOut(this.getSlideAnchor(), {
46778             callback: function(){
46779                 this.el.setLeftTop(-10000, -10000);
46780                 this.afterSlide();
46781                 this.afterSlideIn();
46782                 Roo.callback(cb);
46783             },
46784             scope: this,
46785             block: true
46786         });
46787     },
46788     
46789     slideInIf : function(e){
46790         if(!e.within(this.el)){
46791             this.slideIn();
46792         }
46793     },
46794
46795     animateCollapse : function(){
46796         this.beforeSlide();
46797         this.el.setStyle("z-index", 20000);
46798         var anchor = this.getSlideAnchor();
46799         this.el.slideOut(anchor, {
46800             callback : function(){
46801                 this.el.setStyle("z-index", "");
46802                 this.collapsedEl.slideIn(anchor, {duration:.3});
46803                 this.afterSlide();
46804                 this.el.setLocation(-10000,-10000);
46805                 this.el.hide();
46806                 this.fireEvent("collapsed", this);
46807             },
46808             scope: this,
46809             block: true
46810         });
46811     },
46812
46813     animateExpand : function(){
46814         this.beforeSlide();
46815         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46816         this.el.setStyle("z-index", 20000);
46817         this.collapsedEl.hide({
46818             duration:.1
46819         });
46820         this.el.slideIn(this.getSlideAnchor(), {
46821             callback : function(){
46822                 this.el.setStyle("z-index", "");
46823                 this.afterSlide();
46824                 if(this.split){
46825                     this.split.el.show();
46826                 }
46827                 this.fireEvent("invalidated", this);
46828                 this.fireEvent("expanded", this);
46829             },
46830             scope: this,
46831             block: true
46832         });
46833     },
46834
46835     anchors : {
46836         "west" : "left",
46837         "east" : "right",
46838         "north" : "top",
46839         "south" : "bottom"
46840     },
46841
46842     sanchors : {
46843         "west" : "l",
46844         "east" : "r",
46845         "north" : "t",
46846         "south" : "b"
46847     },
46848
46849     canchors : {
46850         "west" : "tl-tr",
46851         "east" : "tr-tl",
46852         "north" : "tl-bl",
46853         "south" : "bl-tl"
46854     },
46855
46856     getAnchor : function(){
46857         return this.anchors[this.position];
46858     },
46859
46860     getCollapseAnchor : function(){
46861         return this.canchors[this.position];
46862     },
46863
46864     getSlideAnchor : function(){
46865         return this.sanchors[this.position];
46866     },
46867
46868     getAlignAdj : function(){
46869         var cm = this.cmargins;
46870         switch(this.position){
46871             case "west":
46872                 return [0, 0];
46873             break;
46874             case "east":
46875                 return [0, 0];
46876             break;
46877             case "north":
46878                 return [0, 0];
46879             break;
46880             case "south":
46881                 return [0, 0];
46882             break;
46883         }
46884     },
46885
46886     getExpandAdj : function(){
46887         var c = this.collapsedEl, cm = this.cmargins;
46888         switch(this.position){
46889             case "west":
46890                 return [-(cm.right+c.getWidth()+cm.left), 0];
46891             break;
46892             case "east":
46893                 return [cm.right+c.getWidth()+cm.left, 0];
46894             break;
46895             case "north":
46896                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46897             break;
46898             case "south":
46899                 return [0, cm.top+cm.bottom+c.getHeight()];
46900             break;
46901         }
46902     }
46903 });/*
46904  * Based on:
46905  * Ext JS Library 1.1.1
46906  * Copyright(c) 2006-2007, Ext JS, LLC.
46907  *
46908  * Originally Released Under LGPL - original licence link has changed is not relivant.
46909  *
46910  * Fork - LGPL
46911  * <script type="text/javascript">
46912  */
46913 /*
46914  * These classes are private internal classes
46915  */
46916 Roo.CenterLayoutRegion = function(mgr, config){
46917     Roo.LayoutRegion.call(this, mgr, config, "center");
46918     this.visible = true;
46919     this.minWidth = config.minWidth || 20;
46920     this.minHeight = config.minHeight || 20;
46921 };
46922
46923 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46924     hide : function(){
46925         // center panel can't be hidden
46926     },
46927     
46928     show : function(){
46929         // center panel can't be hidden
46930     },
46931     
46932     getMinWidth: function(){
46933         return this.minWidth;
46934     },
46935     
46936     getMinHeight: function(){
46937         return this.minHeight;
46938     }
46939 });
46940
46941
46942 Roo.NorthLayoutRegion = function(mgr, config){
46943     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46944     if(this.split){
46945         this.split.placement = Roo.SplitBar.TOP;
46946         this.split.orientation = Roo.SplitBar.VERTICAL;
46947         this.split.el.addClass("x-layout-split-v");
46948     }
46949     var size = config.initialSize || config.height;
46950     if(typeof size != "undefined"){
46951         this.el.setHeight(size);
46952     }
46953 };
46954 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46955     orientation: Roo.SplitBar.VERTICAL,
46956     getBox : function(){
46957         if(this.collapsed){
46958             return this.collapsedEl.getBox();
46959         }
46960         var box = this.el.getBox();
46961         if(this.split){
46962             box.height += this.split.el.getHeight();
46963         }
46964         return box;
46965     },
46966     
46967     updateBox : function(box){
46968         if(this.split && !this.collapsed){
46969             box.height -= this.split.el.getHeight();
46970             this.split.el.setLeft(box.x);
46971             this.split.el.setTop(box.y+box.height);
46972             this.split.el.setWidth(box.width);
46973         }
46974         if(this.collapsed){
46975             this.updateBody(box.width, null);
46976         }
46977         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46978     }
46979 });
46980
46981 Roo.SouthLayoutRegion = function(mgr, config){
46982     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46983     if(this.split){
46984         this.split.placement = Roo.SplitBar.BOTTOM;
46985         this.split.orientation = Roo.SplitBar.VERTICAL;
46986         this.split.el.addClass("x-layout-split-v");
46987     }
46988     var size = config.initialSize || config.height;
46989     if(typeof size != "undefined"){
46990         this.el.setHeight(size);
46991     }
46992 };
46993 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46994     orientation: Roo.SplitBar.VERTICAL,
46995     getBox : function(){
46996         if(this.collapsed){
46997             return this.collapsedEl.getBox();
46998         }
46999         var box = this.el.getBox();
47000         if(this.split){
47001             var sh = this.split.el.getHeight();
47002             box.height += sh;
47003             box.y -= sh;
47004         }
47005         return box;
47006     },
47007     
47008     updateBox : function(box){
47009         if(this.split && !this.collapsed){
47010             var sh = this.split.el.getHeight();
47011             box.height -= sh;
47012             box.y += sh;
47013             this.split.el.setLeft(box.x);
47014             this.split.el.setTop(box.y-sh);
47015             this.split.el.setWidth(box.width);
47016         }
47017         if(this.collapsed){
47018             this.updateBody(box.width, null);
47019         }
47020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47021     }
47022 });
47023
47024 Roo.EastLayoutRegion = function(mgr, config){
47025     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
47026     if(this.split){
47027         this.split.placement = Roo.SplitBar.RIGHT;
47028         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47029         this.split.el.addClass("x-layout-split-h");
47030     }
47031     var size = config.initialSize || config.width;
47032     if(typeof size != "undefined"){
47033         this.el.setWidth(size);
47034     }
47035 };
47036 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
47037     orientation: Roo.SplitBar.HORIZONTAL,
47038     getBox : function(){
47039         if(this.collapsed){
47040             return this.collapsedEl.getBox();
47041         }
47042         var box = this.el.getBox();
47043         if(this.split){
47044             var sw = this.split.el.getWidth();
47045             box.width += sw;
47046             box.x -= sw;
47047         }
47048         return box;
47049     },
47050
47051     updateBox : function(box){
47052         if(this.split && !this.collapsed){
47053             var sw = this.split.el.getWidth();
47054             box.width -= sw;
47055             this.split.el.setLeft(box.x);
47056             this.split.el.setTop(box.y);
47057             this.split.el.setHeight(box.height);
47058             box.x += sw;
47059         }
47060         if(this.collapsed){
47061             this.updateBody(null, box.height);
47062         }
47063         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47064     }
47065 });
47066
47067 Roo.WestLayoutRegion = function(mgr, config){
47068     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
47069     if(this.split){
47070         this.split.placement = Roo.SplitBar.LEFT;
47071         this.split.orientation = Roo.SplitBar.HORIZONTAL;
47072         this.split.el.addClass("x-layout-split-h");
47073     }
47074     var size = config.initialSize || config.width;
47075     if(typeof size != "undefined"){
47076         this.el.setWidth(size);
47077     }
47078 };
47079 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
47080     orientation: Roo.SplitBar.HORIZONTAL,
47081     getBox : function(){
47082         if(this.collapsed){
47083             return this.collapsedEl.getBox();
47084         }
47085         var box = this.el.getBox();
47086         if(this.split){
47087             box.width += this.split.el.getWidth();
47088         }
47089         return box;
47090     },
47091     
47092     updateBox : function(box){
47093         if(this.split && !this.collapsed){
47094             var sw = this.split.el.getWidth();
47095             box.width -= sw;
47096             this.split.el.setLeft(box.x+box.width);
47097             this.split.el.setTop(box.y);
47098             this.split.el.setHeight(box.height);
47099         }
47100         if(this.collapsed){
47101             this.updateBody(null, box.height);
47102         }
47103         Roo.LayoutRegion.prototype.updateBox.call(this, box);
47104     }
47105 });
47106 /*
47107  * Based on:
47108  * Ext JS Library 1.1.1
47109  * Copyright(c) 2006-2007, Ext JS, LLC.
47110  *
47111  * Originally Released Under LGPL - original licence link has changed is not relivant.
47112  *
47113  * Fork - LGPL
47114  * <script type="text/javascript">
47115  */
47116  
47117  
47118 /*
47119  * Private internal class for reading and applying state
47120  */
47121 Roo.LayoutStateManager = function(layout){
47122      // default empty state
47123      this.state = {
47124         north: {},
47125         south: {},
47126         east: {},
47127         west: {}       
47128     };
47129 };
47130
47131 Roo.LayoutStateManager.prototype = {
47132     init : function(layout, provider){
47133         this.provider = provider;
47134         var state = provider.get(layout.id+"-layout-state");
47135         if(state){
47136             var wasUpdating = layout.isUpdating();
47137             if(!wasUpdating){
47138                 layout.beginUpdate();
47139             }
47140             for(var key in state){
47141                 if(typeof state[key] != "function"){
47142                     var rstate = state[key];
47143                     var r = layout.getRegion(key);
47144                     if(r && rstate){
47145                         if(rstate.size){
47146                             r.resizeTo(rstate.size);
47147                         }
47148                         if(rstate.collapsed == true){
47149                             r.collapse(true);
47150                         }else{
47151                             r.expand(null, true);
47152                         }
47153                     }
47154                 }
47155             }
47156             if(!wasUpdating){
47157                 layout.endUpdate();
47158             }
47159             this.state = state; 
47160         }
47161         this.layout = layout;
47162         layout.on("regionresized", this.onRegionResized, this);
47163         layout.on("regioncollapsed", this.onRegionCollapsed, this);
47164         layout.on("regionexpanded", this.onRegionExpanded, this);
47165     },
47166     
47167     storeState : function(){
47168         this.provider.set(this.layout.id+"-layout-state", this.state);
47169     },
47170     
47171     onRegionResized : function(region, newSize){
47172         this.state[region.getPosition()].size = newSize;
47173         this.storeState();
47174     },
47175     
47176     onRegionCollapsed : function(region){
47177         this.state[region.getPosition()].collapsed = true;
47178         this.storeState();
47179     },
47180     
47181     onRegionExpanded : function(region){
47182         this.state[region.getPosition()].collapsed = false;
47183         this.storeState();
47184     }
47185 };/*
47186  * Based on:
47187  * Ext JS Library 1.1.1
47188  * Copyright(c) 2006-2007, Ext JS, LLC.
47189  *
47190  * Originally Released Under LGPL - original licence link has changed is not relivant.
47191  *
47192  * Fork - LGPL
47193  * <script type="text/javascript">
47194  */
47195 /**
47196  * @class Roo.ContentPanel
47197  * @extends Roo.util.Observable
47198  * A basic ContentPanel element.
47199  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
47200  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
47201  * @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
47202  * @cfg {Boolean}   closable      True if the panel can be closed/removed
47203  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
47204  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
47205  * @cfg {Toolbar}   toolbar       A toolbar for this panel
47206  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
47207  * @cfg {String} title          The title for this panel
47208  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
47209  * @cfg {String} url            Calls {@link #setUrl} with this value
47210  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
47211  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
47212  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
47213  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
47214
47215  * @constructor
47216  * Create a new ContentPanel.
47217  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
47218  * @param {String/Object} config A string to set only the title or a config object
47219  * @param {String} content (optional) Set the HTML content for this panel
47220  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
47221  */
47222 Roo.ContentPanel = function(el, config, content){
47223     
47224      
47225     /*
47226     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
47227         config = el;
47228         el = Roo.id();
47229     }
47230     if (config && config.parentLayout) { 
47231         el = config.parentLayout.el.createChild(); 
47232     }
47233     */
47234     if(el.autoCreate){ // xtype is available if this is called from factory
47235         config = el;
47236         el = Roo.id();
47237     }
47238     this.el = Roo.get(el);
47239     if(!this.el && config && config.autoCreate){
47240         if(typeof config.autoCreate == "object"){
47241             if(!config.autoCreate.id){
47242                 config.autoCreate.id = config.id||el;
47243             }
47244             this.el = Roo.DomHelper.append(document.body,
47245                         config.autoCreate, true);
47246         }else{
47247             this.el = Roo.DomHelper.append(document.body,
47248                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
47249         }
47250     }
47251     this.closable = false;
47252     this.loaded = false;
47253     this.active = false;
47254     if(typeof config == "string"){
47255         this.title = config;
47256     }else{
47257         Roo.apply(this, config);
47258     }
47259     
47260     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
47261         this.wrapEl = this.el.wrap();
47262         this.toolbar.container = this.el.insertSibling(false, 'before');
47263         this.toolbar = new Roo.Toolbar(this.toolbar);
47264     }
47265     
47266     
47267     
47268     if(this.resizeEl){
47269         this.resizeEl = Roo.get(this.resizeEl, true);
47270     }else{
47271         this.resizeEl = this.el;
47272     }
47273     this.addEvents({
47274         /**
47275          * @event activate
47276          * Fires when this panel is activated. 
47277          * @param {Roo.ContentPanel} this
47278          */
47279         "activate" : true,
47280         /**
47281          * @event deactivate
47282          * Fires when this panel is activated. 
47283          * @param {Roo.ContentPanel} this
47284          */
47285         "deactivate" : true,
47286
47287         /**
47288          * @event resize
47289          * Fires when this panel is resized if fitToFrame is true.
47290          * @param {Roo.ContentPanel} this
47291          * @param {Number} width The width after any component adjustments
47292          * @param {Number} height The height after any component adjustments
47293          */
47294         "resize" : true,
47295         
47296          /**
47297          * @event render
47298          * Fires when this tab is created
47299          * @param {Roo.ContentPanel} this
47300          */
47301         "render" : true
47302         
47303         
47304         
47305     });
47306     if(this.autoScroll){
47307         this.resizeEl.setStyle("overflow", "auto");
47308     } else {
47309         // fix randome scrolling
47310         this.el.on('scroll', function() {
47311             Roo.log('fix random scolling');
47312             this.scrollTo('top',0); 
47313         });
47314     }
47315     content = content || this.content;
47316     if(content){
47317         this.setContent(content);
47318     }
47319     if(config && config.url){
47320         this.setUrl(this.url, this.params, this.loadOnce);
47321     }
47322     
47323     
47324     
47325     Roo.ContentPanel.superclass.constructor.call(this);
47326     
47327     this.fireEvent('render', this);
47328 };
47329
47330 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
47331     tabTip:'',
47332     setRegion : function(region){
47333         this.region = region;
47334         if(region){
47335            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
47336         }else{
47337            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
47338         } 
47339     },
47340     
47341     /**
47342      * Returns the toolbar for this Panel if one was configured. 
47343      * @return {Roo.Toolbar} 
47344      */
47345     getToolbar : function(){
47346         return this.toolbar;
47347     },
47348     
47349     setActiveState : function(active){
47350         this.active = active;
47351         if(!active){
47352             this.fireEvent("deactivate", this);
47353         }else{
47354             this.fireEvent("activate", this);
47355         }
47356     },
47357     /**
47358      * Updates this panel's element
47359      * @param {String} content The new content
47360      * @param {Boolean} loadScripts (optional) true to look for and process scripts
47361     */
47362     setContent : function(content, loadScripts){
47363         this.el.update(content, loadScripts);
47364     },
47365
47366     ignoreResize : function(w, h){
47367         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
47368             return true;
47369         }else{
47370             this.lastSize = {width: w, height: h};
47371             return false;
47372         }
47373     },
47374     /**
47375      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
47376      * @return {Roo.UpdateManager} The UpdateManager
47377      */
47378     getUpdateManager : function(){
47379         return this.el.getUpdateManager();
47380     },
47381      /**
47382      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
47383      * @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:
47384 <pre><code>
47385 panel.load({
47386     url: "your-url.php",
47387     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
47388     callback: yourFunction,
47389     scope: yourObject, //(optional scope)
47390     discardUrl: false,
47391     nocache: false,
47392     text: "Loading...",
47393     timeout: 30,
47394     scripts: false
47395 });
47396 </code></pre>
47397      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
47398      * 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.
47399      * @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}
47400      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
47401      * @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.
47402      * @return {Roo.ContentPanel} this
47403      */
47404     load : function(){
47405         var um = this.el.getUpdateManager();
47406         um.update.apply(um, arguments);
47407         return this;
47408     },
47409
47410
47411     /**
47412      * 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.
47413      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
47414      * @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)
47415      * @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)
47416      * @return {Roo.UpdateManager} The UpdateManager
47417      */
47418     setUrl : function(url, params, loadOnce){
47419         if(this.refreshDelegate){
47420             this.removeListener("activate", this.refreshDelegate);
47421         }
47422         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47423         this.on("activate", this.refreshDelegate);
47424         return this.el.getUpdateManager();
47425     },
47426     
47427     _handleRefresh : function(url, params, loadOnce){
47428         if(!loadOnce || !this.loaded){
47429             var updater = this.el.getUpdateManager();
47430             updater.update(url, params, this._setLoaded.createDelegate(this));
47431         }
47432     },
47433     
47434     _setLoaded : function(){
47435         this.loaded = true;
47436     }, 
47437     
47438     /**
47439      * Returns this panel's id
47440      * @return {String} 
47441      */
47442     getId : function(){
47443         return this.el.id;
47444     },
47445     
47446     /** 
47447      * Returns this panel's element - used by regiosn to add.
47448      * @return {Roo.Element} 
47449      */
47450     getEl : function(){
47451         return this.wrapEl || this.el;
47452     },
47453     
47454     adjustForComponents : function(width, height){
47455         if(this.resizeEl != this.el){
47456             width -= this.el.getFrameWidth('lr');
47457             height -= this.el.getFrameWidth('tb');
47458         }
47459         if(this.toolbar){
47460             var te = this.toolbar.getEl();
47461             height -= te.getHeight();
47462             te.setWidth(width);
47463         }
47464         if(this.adjustments){
47465             width += this.adjustments[0];
47466             height += this.adjustments[1];
47467         }
47468         return {"width": width, "height": height};
47469     },
47470     
47471     setSize : function(width, height){
47472         if(this.fitToFrame && !this.ignoreResize(width, height)){
47473             if(this.fitContainer && this.resizeEl != this.el){
47474                 this.el.setSize(width, height);
47475             }
47476             var size = this.adjustForComponents(width, height);
47477             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
47478             this.fireEvent('resize', this, size.width, size.height);
47479         }
47480     },
47481     
47482     /**
47483      * Returns this panel's title
47484      * @return {String} 
47485      */
47486     getTitle : function(){
47487         return this.title;
47488     },
47489     
47490     /**
47491      * Set this panel's title
47492      * @param {String} title
47493      */
47494     setTitle : function(title){
47495         this.title = title;
47496         if(this.region){
47497             this.region.updatePanelTitle(this, title);
47498         }
47499     },
47500     
47501     /**
47502      * Returns true is this panel was configured to be closable
47503      * @return {Boolean} 
47504      */
47505     isClosable : function(){
47506         return this.closable;
47507     },
47508     
47509     beforeSlide : function(){
47510         this.el.clip();
47511         this.resizeEl.clip();
47512     },
47513     
47514     afterSlide : function(){
47515         this.el.unclip();
47516         this.resizeEl.unclip();
47517     },
47518     
47519     /**
47520      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
47521      *   Will fail silently if the {@link #setUrl} method has not been called.
47522      *   This does not activate the panel, just updates its content.
47523      */
47524     refresh : function(){
47525         if(this.refreshDelegate){
47526            this.loaded = false;
47527            this.refreshDelegate();
47528         }
47529     },
47530     
47531     /**
47532      * Destroys this panel
47533      */
47534     destroy : function(){
47535         this.el.removeAllListeners();
47536         var tempEl = document.createElement("span");
47537         tempEl.appendChild(this.el.dom);
47538         tempEl.innerHTML = "";
47539         this.el.remove();
47540         this.el = null;
47541     },
47542     
47543     /**
47544      * form - if the content panel contains a form - this is a reference to it.
47545      * @type {Roo.form.Form}
47546      */
47547     form : false,
47548     /**
47549      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
47550      *    This contains a reference to it.
47551      * @type {Roo.View}
47552      */
47553     view : false,
47554     
47555       /**
47556      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
47557      * <pre><code>
47558
47559 layout.addxtype({
47560        xtype : 'Form',
47561        items: [ .... ]
47562    }
47563 );
47564
47565 </code></pre>
47566      * @param {Object} cfg Xtype definition of item to add.
47567      */
47568     
47569     addxtype : function(cfg) {
47570         // add form..
47571         if (cfg.xtype.match(/^Form$/)) {
47572             var el = this.el.createChild();
47573
47574             this.form = new  Roo.form.Form(cfg);
47575             
47576             
47577             if ( this.form.allItems.length) this.form.render(el.dom);
47578             return this.form;
47579         }
47580         // should only have one of theses..
47581         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47582             // views..
47583             cfg.el = this.el.appendChild(document.createElement("div"));
47584             // factory?
47585             
47586             var ret = new Roo.factory(cfg);
47587             ret.render && ret.render(false, ''); // render blank..
47588             this.view = ret;
47589             return ret;
47590         }
47591         return false;
47592     }
47593 });
47594
47595 /**
47596  * @class Roo.GridPanel
47597  * @extends Roo.ContentPanel
47598  * @constructor
47599  * Create a new GridPanel.
47600  * @param {Roo.grid.Grid} grid The grid for this panel
47601  * @param {String/Object} config A string to set only the panel's title, or a config object
47602  */
47603 Roo.GridPanel = function(grid, config){
47604     
47605   
47606     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47607         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47608         
47609     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47610     
47611     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47612     
47613     if(this.toolbar){
47614         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47615     }
47616     // xtype created footer. - not sure if will work as we normally have to render first..
47617     if (this.footer && !this.footer.el && this.footer.xtype) {
47618         
47619         this.footer.container = this.grid.getView().getFooterPanel(true);
47620         this.footer.dataSource = this.grid.dataSource;
47621         this.footer = Roo.factory(this.footer, Roo);
47622         
47623     }
47624     
47625     grid.monitorWindowResize = false; // turn off autosizing
47626     grid.autoHeight = false;
47627     grid.autoWidth = false;
47628     this.grid = grid;
47629     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47630 };
47631
47632 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47633     getId : function(){
47634         return this.grid.id;
47635     },
47636     
47637     /**
47638      * Returns the grid for this panel
47639      * @return {Roo.grid.Grid} 
47640      */
47641     getGrid : function(){
47642         return this.grid;    
47643     },
47644     
47645     setSize : function(width, height){
47646         if(!this.ignoreResize(width, height)){
47647             var grid = this.grid;
47648             var size = this.adjustForComponents(width, height);
47649             grid.getGridEl().setSize(size.width, size.height);
47650             grid.autoSize();
47651         }
47652     },
47653     
47654     beforeSlide : function(){
47655         this.grid.getView().scroller.clip();
47656     },
47657     
47658     afterSlide : function(){
47659         this.grid.getView().scroller.unclip();
47660     },
47661     
47662     destroy : function(){
47663         this.grid.destroy();
47664         delete this.grid;
47665         Roo.GridPanel.superclass.destroy.call(this); 
47666     }
47667 });
47668
47669
47670 /**
47671  * @class Roo.NestedLayoutPanel
47672  * @extends Roo.ContentPanel
47673  * @constructor
47674  * Create a new NestedLayoutPanel.
47675  * 
47676  * 
47677  * @param {Roo.BorderLayout} layout The layout for this panel
47678  * @param {String/Object} config A string to set only the title or a config object
47679  */
47680 Roo.NestedLayoutPanel = function(layout, config)
47681 {
47682     // construct with only one argument..
47683     /* FIXME - implement nicer consturctors
47684     if (layout.layout) {
47685         config = layout;
47686         layout = config.layout;
47687         delete config.layout;
47688     }
47689     if (layout.xtype && !layout.getEl) {
47690         // then layout needs constructing..
47691         layout = Roo.factory(layout, Roo);
47692     }
47693     */
47694     
47695     
47696     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47697     
47698     layout.monitorWindowResize = false; // turn off autosizing
47699     this.layout = layout;
47700     this.layout.getEl().addClass("x-layout-nested-layout");
47701     
47702     
47703     
47704     
47705 };
47706
47707 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47708
47709     setSize : function(width, height){
47710         if(!this.ignoreResize(width, height)){
47711             var size = this.adjustForComponents(width, height);
47712             var el = this.layout.getEl();
47713             el.setSize(size.width, size.height);
47714             var touch = el.dom.offsetWidth;
47715             this.layout.layout();
47716             // ie requires a double layout on the first pass
47717             if(Roo.isIE && !this.initialized){
47718                 this.initialized = true;
47719                 this.layout.layout();
47720             }
47721         }
47722     },
47723     
47724     // activate all subpanels if not currently active..
47725     
47726     setActiveState : function(active){
47727         this.active = active;
47728         if(!active){
47729             this.fireEvent("deactivate", this);
47730             return;
47731         }
47732         
47733         this.fireEvent("activate", this);
47734         // not sure if this should happen before or after..
47735         if (!this.layout) {
47736             return; // should not happen..
47737         }
47738         var reg = false;
47739         for (var r in this.layout.regions) {
47740             reg = this.layout.getRegion(r);
47741             if (reg.getActivePanel()) {
47742                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47743                 reg.setActivePanel(reg.getActivePanel());
47744                 continue;
47745             }
47746             if (!reg.panels.length) {
47747                 continue;
47748             }
47749             reg.showPanel(reg.getPanel(0));
47750         }
47751         
47752         
47753         
47754         
47755     },
47756     
47757     /**
47758      * Returns the nested BorderLayout for this panel
47759      * @return {Roo.BorderLayout} 
47760      */
47761     getLayout : function(){
47762         return this.layout;
47763     },
47764     
47765      /**
47766      * Adds a xtype elements to the layout of the nested panel
47767      * <pre><code>
47768
47769 panel.addxtype({
47770        xtype : 'ContentPanel',
47771        region: 'west',
47772        items: [ .... ]
47773    }
47774 );
47775
47776 panel.addxtype({
47777         xtype : 'NestedLayoutPanel',
47778         region: 'west',
47779         layout: {
47780            center: { },
47781            west: { }   
47782         },
47783         items : [ ... list of content panels or nested layout panels.. ]
47784    }
47785 );
47786 </code></pre>
47787      * @param {Object} cfg Xtype definition of item to add.
47788      */
47789     addxtype : function(cfg) {
47790         return this.layout.addxtype(cfg);
47791     
47792     }
47793 });
47794
47795 Roo.ScrollPanel = function(el, config, content){
47796     config = config || {};
47797     config.fitToFrame = true;
47798     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47799     
47800     this.el.dom.style.overflow = "hidden";
47801     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47802     this.el.removeClass("x-layout-inactive-content");
47803     this.el.on("mousewheel", this.onWheel, this);
47804
47805     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47806     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47807     up.unselectable(); down.unselectable();
47808     up.on("click", this.scrollUp, this);
47809     down.on("click", this.scrollDown, this);
47810     up.addClassOnOver("x-scroller-btn-over");
47811     down.addClassOnOver("x-scroller-btn-over");
47812     up.addClassOnClick("x-scroller-btn-click");
47813     down.addClassOnClick("x-scroller-btn-click");
47814     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47815
47816     this.resizeEl = this.el;
47817     this.el = wrap; this.up = up; this.down = down;
47818 };
47819
47820 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47821     increment : 100,
47822     wheelIncrement : 5,
47823     scrollUp : function(){
47824         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47825     },
47826
47827     scrollDown : function(){
47828         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47829     },
47830
47831     afterScroll : function(){
47832         var el = this.resizeEl;
47833         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47834         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47835         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47836     },
47837
47838     setSize : function(){
47839         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47840         this.afterScroll();
47841     },
47842
47843     onWheel : function(e){
47844         var d = e.getWheelDelta();
47845         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47846         this.afterScroll();
47847         e.stopEvent();
47848     },
47849
47850     setContent : function(content, loadScripts){
47851         this.resizeEl.update(content, loadScripts);
47852     }
47853
47854 });
47855
47856
47857
47858
47859
47860
47861
47862
47863
47864 /**
47865  * @class Roo.TreePanel
47866  * @extends Roo.ContentPanel
47867  * @constructor
47868  * Create a new TreePanel. - defaults to fit/scoll contents.
47869  * @param {String/Object} config A string to set only the panel's title, or a config object
47870  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47871  */
47872 Roo.TreePanel = function(config){
47873     var el = config.el;
47874     var tree = config.tree;
47875     delete config.tree; 
47876     delete config.el; // hopefull!
47877     
47878     // wrapper for IE7 strict & safari scroll issue
47879     
47880     var treeEl = el.createChild();
47881     config.resizeEl = treeEl;
47882     
47883     
47884     
47885     Roo.TreePanel.superclass.constructor.call(this, el, config);
47886  
47887  
47888     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47889     //console.log(tree);
47890     this.on('activate', function()
47891     {
47892         if (this.tree.rendered) {
47893             return;
47894         }
47895         //console.log('render tree');
47896         this.tree.render();
47897     });
47898     
47899     this.on('resize',  function (cp, w, h) {
47900             this.tree.innerCt.setWidth(w);
47901             this.tree.innerCt.setHeight(h);
47902             this.tree.innerCt.setStyle('overflow-y', 'auto');
47903     });
47904
47905         
47906     
47907 };
47908
47909 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47910     fitToFrame : true,
47911     autoScroll : true
47912 });
47913
47914
47915
47916
47917
47918
47919
47920
47921
47922
47923
47924 /*
47925  * Based on:
47926  * Ext JS Library 1.1.1
47927  * Copyright(c) 2006-2007, Ext JS, LLC.
47928  *
47929  * Originally Released Under LGPL - original licence link has changed is not relivant.
47930  *
47931  * Fork - LGPL
47932  * <script type="text/javascript">
47933  */
47934  
47935
47936 /**
47937  * @class Roo.ReaderLayout
47938  * @extends Roo.BorderLayout
47939  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47940  * center region containing two nested regions (a top one for a list view and one for item preview below),
47941  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47942  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47943  * expedites the setup of the overall layout and regions for this common application style.
47944  * Example:
47945  <pre><code>
47946 var reader = new Roo.ReaderLayout();
47947 var CP = Roo.ContentPanel;  // shortcut for adding
47948
47949 reader.beginUpdate();
47950 reader.add("north", new CP("north", "North"));
47951 reader.add("west", new CP("west", {title: "West"}));
47952 reader.add("east", new CP("east", {title: "East"}));
47953
47954 reader.regions.listView.add(new CP("listView", "List"));
47955 reader.regions.preview.add(new CP("preview", "Preview"));
47956 reader.endUpdate();
47957 </code></pre>
47958 * @constructor
47959 * Create a new ReaderLayout
47960 * @param {Object} config Configuration options
47961 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47962 * document.body if omitted)
47963 */
47964 Roo.ReaderLayout = function(config, renderTo){
47965     var c = config || {size:{}};
47966     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47967         north: c.north !== false ? Roo.apply({
47968             split:false,
47969             initialSize: 32,
47970             titlebar: false
47971         }, c.north) : false,
47972         west: c.west !== false ? Roo.apply({
47973             split:true,
47974             initialSize: 200,
47975             minSize: 175,
47976             maxSize: 400,
47977             titlebar: true,
47978             collapsible: true,
47979             animate: true,
47980             margins:{left:5,right:0,bottom:5,top:5},
47981             cmargins:{left:5,right:5,bottom:5,top:5}
47982         }, c.west) : false,
47983         east: c.east !== false ? Roo.apply({
47984             split:true,
47985             initialSize: 200,
47986             minSize: 175,
47987             maxSize: 400,
47988             titlebar: true,
47989             collapsible: true,
47990             animate: true,
47991             margins:{left:0,right:5,bottom:5,top:5},
47992             cmargins:{left:5,right:5,bottom:5,top:5}
47993         }, c.east) : false,
47994         center: Roo.apply({
47995             tabPosition: 'top',
47996             autoScroll:false,
47997             closeOnTab: true,
47998             titlebar:false,
47999             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
48000         }, c.center)
48001     });
48002
48003     this.el.addClass('x-reader');
48004
48005     this.beginUpdate();
48006
48007     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
48008         south: c.preview !== false ? Roo.apply({
48009             split:true,
48010             initialSize: 200,
48011             minSize: 100,
48012             autoScroll:true,
48013             collapsible:true,
48014             titlebar: true,
48015             cmargins:{top:5,left:0, right:0, bottom:0}
48016         }, c.preview) : false,
48017         center: Roo.apply({
48018             autoScroll:false,
48019             titlebar:false,
48020             minHeight:200
48021         }, c.listView)
48022     });
48023     this.add('center', new Roo.NestedLayoutPanel(inner,
48024             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
48025
48026     this.endUpdate();
48027
48028     this.regions.preview = inner.getRegion('south');
48029     this.regions.listView = inner.getRegion('center');
48030 };
48031
48032 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
48033  * Based on:
48034  * Ext JS Library 1.1.1
48035  * Copyright(c) 2006-2007, Ext JS, LLC.
48036  *
48037  * Originally Released Under LGPL - original licence link has changed is not relivant.
48038  *
48039  * Fork - LGPL
48040  * <script type="text/javascript">
48041  */
48042  
48043 /**
48044  * @class Roo.grid.Grid
48045  * @extends Roo.util.Observable
48046  * This class represents the primary interface of a component based grid control.
48047  * <br><br>Usage:<pre><code>
48048  var grid = new Roo.grid.Grid("my-container-id", {
48049      ds: myDataStore,
48050      cm: myColModel,
48051      selModel: mySelectionModel,
48052      autoSizeColumns: true,
48053      monitorWindowResize: false,
48054      trackMouseOver: true
48055  });
48056  // set any options
48057  grid.render();
48058  * </code></pre>
48059  * <b>Common Problems:</b><br/>
48060  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
48061  * element will correct this<br/>
48062  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
48063  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
48064  * are unpredictable.<br/>
48065  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
48066  * grid to calculate dimensions/offsets.<br/>
48067   * @constructor
48068  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
48069  * The container MUST have some type of size defined for the grid to fill. The container will be
48070  * automatically set to position relative if it isn't already.
48071  * @param {Object} config A config object that sets properties on this grid.
48072  */
48073 Roo.grid.Grid = function(container, config){
48074         // initialize the container
48075         this.container = Roo.get(container);
48076         this.container.update("");
48077         this.container.setStyle("overflow", "hidden");
48078     this.container.addClass('x-grid-container');
48079
48080     this.id = this.container.id;
48081
48082     Roo.apply(this, config);
48083     // check and correct shorthanded configs
48084     if(this.ds){
48085         this.dataSource = this.ds;
48086         delete this.ds;
48087     }
48088     if(this.cm){
48089         this.colModel = this.cm;
48090         delete this.cm;
48091     }
48092     if(this.sm){
48093         this.selModel = this.sm;
48094         delete this.sm;
48095     }
48096
48097     if (this.selModel) {
48098         this.selModel = Roo.factory(this.selModel, Roo.grid);
48099         this.sm = this.selModel;
48100         this.sm.xmodule = this.xmodule || false;
48101     }
48102     if (typeof(this.colModel.config) == 'undefined') {
48103         this.colModel = new Roo.grid.ColumnModel(this.colModel);
48104         this.cm = this.colModel;
48105         this.cm.xmodule = this.xmodule || false;
48106     }
48107     if (this.dataSource) {
48108         this.dataSource= Roo.factory(this.dataSource, Roo.data);
48109         this.ds = this.dataSource;
48110         this.ds.xmodule = this.xmodule || false;
48111          
48112     }
48113     
48114     
48115     
48116     if(this.width){
48117         this.container.setWidth(this.width);
48118     }
48119
48120     if(this.height){
48121         this.container.setHeight(this.height);
48122     }
48123     /** @private */
48124         this.addEvents({
48125         // raw events
48126         /**
48127          * @event click
48128          * The raw click event for the entire grid.
48129          * @param {Roo.EventObject} e
48130          */
48131         "click" : true,
48132         /**
48133          * @event dblclick
48134          * The raw dblclick event for the entire grid.
48135          * @param {Roo.EventObject} e
48136          */
48137         "dblclick" : true,
48138         /**
48139          * @event contextmenu
48140          * The raw contextmenu event for the entire grid.
48141          * @param {Roo.EventObject} e
48142          */
48143         "contextmenu" : true,
48144         /**
48145          * @event mousedown
48146          * The raw mousedown event for the entire grid.
48147          * @param {Roo.EventObject} e
48148          */
48149         "mousedown" : true,
48150         /**
48151          * @event mouseup
48152          * The raw mouseup event for the entire grid.
48153          * @param {Roo.EventObject} e
48154          */
48155         "mouseup" : true,
48156         /**
48157          * @event mouseover
48158          * The raw mouseover event for the entire grid.
48159          * @param {Roo.EventObject} e
48160          */
48161         "mouseover" : true,
48162         /**
48163          * @event mouseout
48164          * The raw mouseout event for the entire grid.
48165          * @param {Roo.EventObject} e
48166          */
48167         "mouseout" : true,
48168         /**
48169          * @event keypress
48170          * The raw keypress event for the entire grid.
48171          * @param {Roo.EventObject} e
48172          */
48173         "keypress" : true,
48174         /**
48175          * @event keydown
48176          * The raw keydown event for the entire grid.
48177          * @param {Roo.EventObject} e
48178          */
48179         "keydown" : true,
48180
48181         // custom events
48182
48183         /**
48184          * @event cellclick
48185          * Fires when a cell is clicked
48186          * @param {Grid} this
48187          * @param {Number} rowIndex
48188          * @param {Number} columnIndex
48189          * @param {Roo.EventObject} e
48190          */
48191         "cellclick" : true,
48192         /**
48193          * @event celldblclick
48194          * Fires when a cell is double clicked
48195          * @param {Grid} this
48196          * @param {Number} rowIndex
48197          * @param {Number} columnIndex
48198          * @param {Roo.EventObject} e
48199          */
48200         "celldblclick" : true,
48201         /**
48202          * @event rowclick
48203          * Fires when a row is clicked
48204          * @param {Grid} this
48205          * @param {Number} rowIndex
48206          * @param {Roo.EventObject} e
48207          */
48208         "rowclick" : true,
48209         /**
48210          * @event rowdblclick
48211          * Fires when a row is double clicked
48212          * @param {Grid} this
48213          * @param {Number} rowIndex
48214          * @param {Roo.EventObject} e
48215          */
48216         "rowdblclick" : true,
48217         /**
48218          * @event headerclick
48219          * Fires when a header is clicked
48220          * @param {Grid} this
48221          * @param {Number} columnIndex
48222          * @param {Roo.EventObject} e
48223          */
48224         "headerclick" : true,
48225         /**
48226          * @event headerdblclick
48227          * Fires when a header cell is double clicked
48228          * @param {Grid} this
48229          * @param {Number} columnIndex
48230          * @param {Roo.EventObject} e
48231          */
48232         "headerdblclick" : true,
48233         /**
48234          * @event rowcontextmenu
48235          * Fires when a row is right clicked
48236          * @param {Grid} this
48237          * @param {Number} rowIndex
48238          * @param {Roo.EventObject} e
48239          */
48240         "rowcontextmenu" : true,
48241         /**
48242          * @event cellcontextmenu
48243          * Fires when a cell is right clicked
48244          * @param {Grid} this
48245          * @param {Number} rowIndex
48246          * @param {Number} cellIndex
48247          * @param {Roo.EventObject} e
48248          */
48249          "cellcontextmenu" : true,
48250         /**
48251          * @event headercontextmenu
48252          * Fires when a header is right clicked
48253          * @param {Grid} this
48254          * @param {Number} columnIndex
48255          * @param {Roo.EventObject} e
48256          */
48257         "headercontextmenu" : true,
48258         /**
48259          * @event bodyscroll
48260          * Fires when the body element is scrolled
48261          * @param {Number} scrollLeft
48262          * @param {Number} scrollTop
48263          */
48264         "bodyscroll" : true,
48265         /**
48266          * @event columnresize
48267          * Fires when the user resizes a column
48268          * @param {Number} columnIndex
48269          * @param {Number} newSize
48270          */
48271         "columnresize" : true,
48272         /**
48273          * @event columnmove
48274          * Fires when the user moves a column
48275          * @param {Number} oldIndex
48276          * @param {Number} newIndex
48277          */
48278         "columnmove" : true,
48279         /**
48280          * @event startdrag
48281          * Fires when row(s) start being dragged
48282          * @param {Grid} this
48283          * @param {Roo.GridDD} dd The drag drop object
48284          * @param {event} e The raw browser event
48285          */
48286         "startdrag" : true,
48287         /**
48288          * @event enddrag
48289          * Fires when a drag operation is complete
48290          * @param {Grid} this
48291          * @param {Roo.GridDD} dd The drag drop object
48292          * @param {event} e The raw browser event
48293          */
48294         "enddrag" : true,
48295         /**
48296          * @event dragdrop
48297          * Fires when dragged row(s) are dropped on a valid DD target
48298          * @param {Grid} this
48299          * @param {Roo.GridDD} dd The drag drop object
48300          * @param {String} targetId The target drag drop object
48301          * @param {event} e The raw browser event
48302          */
48303         "dragdrop" : true,
48304         /**
48305          * @event dragover
48306          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
48307          * @param {Grid} this
48308          * @param {Roo.GridDD} dd The drag drop object
48309          * @param {String} targetId The target drag drop object
48310          * @param {event} e The raw browser event
48311          */
48312         "dragover" : true,
48313         /**
48314          * @event dragenter
48315          *  Fires when the dragged row(s) first cross another DD target while being dragged
48316          * @param {Grid} this
48317          * @param {Roo.GridDD} dd The drag drop object
48318          * @param {String} targetId The target drag drop object
48319          * @param {event} e The raw browser event
48320          */
48321         "dragenter" : true,
48322         /**
48323          * @event dragout
48324          * Fires when the dragged row(s) leave another DD target while being dragged
48325          * @param {Grid} this
48326          * @param {Roo.GridDD} dd The drag drop object
48327          * @param {String} targetId The target drag drop object
48328          * @param {event} e The raw browser event
48329          */
48330         "dragout" : true,
48331         /**
48332          * @event rowclass
48333          * Fires when a row is rendered, so you can change add a style to it.
48334          * @param {GridView} gridview   The grid view
48335          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
48336          */
48337         'rowclass' : true,
48338
48339         /**
48340          * @event render
48341          * Fires when the grid is rendered
48342          * @param {Grid} grid
48343          */
48344         'render' : true
48345     });
48346
48347     Roo.grid.Grid.superclass.constructor.call(this);
48348 };
48349 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
48350     
48351     /**
48352      * @cfg {String} ddGroup - drag drop group.
48353      */
48354
48355     /**
48356      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
48357      */
48358     minColumnWidth : 25,
48359
48360     /**
48361      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
48362      * <b>on initial render.</b> It is more efficient to explicitly size the columns
48363      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
48364      */
48365     autoSizeColumns : false,
48366
48367     /**
48368      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
48369      */
48370     autoSizeHeaders : true,
48371
48372     /**
48373      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
48374      */
48375     monitorWindowResize : true,
48376
48377     /**
48378      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
48379      * rows measured to get a columns size. Default is 0 (all rows).
48380      */
48381     maxRowsToMeasure : 0,
48382
48383     /**
48384      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
48385      */
48386     trackMouseOver : true,
48387
48388     /**
48389     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
48390     */
48391     
48392     /**
48393     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
48394     */
48395     enableDragDrop : false,
48396     
48397     /**
48398     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
48399     */
48400     enableColumnMove : true,
48401     
48402     /**
48403     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
48404     */
48405     enableColumnHide : true,
48406     
48407     /**
48408     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
48409     */
48410     enableRowHeightSync : false,
48411     
48412     /**
48413     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
48414     */
48415     stripeRows : true,
48416     
48417     /**
48418     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
48419     */
48420     autoHeight : false,
48421
48422     /**
48423      * @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.
48424      */
48425     autoExpandColumn : false,
48426
48427     /**
48428     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
48429     * Default is 50.
48430     */
48431     autoExpandMin : 50,
48432
48433     /**
48434     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
48435     */
48436     autoExpandMax : 1000,
48437
48438     /**
48439     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
48440     */
48441     view : null,
48442
48443     /**
48444     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
48445     */
48446     loadMask : false,
48447     /**
48448     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
48449     */
48450     dropTarget: false,
48451     
48452    
48453     
48454     // private
48455     rendered : false,
48456
48457     /**
48458     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
48459     * of a fixed width. Default is false.
48460     */
48461     /**
48462     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
48463     */
48464     /**
48465      * Called once after all setup has been completed and the grid is ready to be rendered.
48466      * @return {Roo.grid.Grid} this
48467      */
48468     render : function()
48469     {
48470         var c = this.container;
48471         // try to detect autoHeight/width mode
48472         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
48473             this.autoHeight = true;
48474         }
48475         var view = this.getView();
48476         view.init(this);
48477
48478         c.on("click", this.onClick, this);
48479         c.on("dblclick", this.onDblClick, this);
48480         c.on("contextmenu", this.onContextMenu, this);
48481         c.on("keydown", this.onKeyDown, this);
48482
48483         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
48484
48485         this.getSelectionModel().init(this);
48486
48487         view.render();
48488
48489         if(this.loadMask){
48490             this.loadMask = new Roo.LoadMask(this.container,
48491                     Roo.apply({store:this.dataSource}, this.loadMask));
48492         }
48493         
48494         
48495         if (this.toolbar && this.toolbar.xtype) {
48496             this.toolbar.container = this.getView().getHeaderPanel(true);
48497             this.toolbar = new Roo.Toolbar(this.toolbar);
48498         }
48499         if (this.footer && this.footer.xtype) {
48500             this.footer.dataSource = this.getDataSource();
48501             this.footer.container = this.getView().getFooterPanel(true);
48502             this.footer = Roo.factory(this.footer, Roo);
48503         }
48504         if (this.dropTarget && this.dropTarget.xtype) {
48505             delete this.dropTarget.xtype;
48506             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
48507         }
48508         
48509         
48510         this.rendered = true;
48511         this.fireEvent('render', this);
48512         return this;
48513     },
48514
48515         /**
48516          * Reconfigures the grid to use a different Store and Column Model.
48517          * The View will be bound to the new objects and refreshed.
48518          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
48519          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
48520          */
48521     reconfigure : function(dataSource, colModel){
48522         if(this.loadMask){
48523             this.loadMask.destroy();
48524             this.loadMask = new Roo.LoadMask(this.container,
48525                     Roo.apply({store:dataSource}, this.loadMask));
48526         }
48527         this.view.bind(dataSource, colModel);
48528         this.dataSource = dataSource;
48529         this.colModel = colModel;
48530         this.view.refresh(true);
48531     },
48532
48533     // private
48534     onKeyDown : function(e){
48535         this.fireEvent("keydown", e);
48536     },
48537
48538     /**
48539      * Destroy this grid.
48540      * @param {Boolean} removeEl True to remove the element
48541      */
48542     destroy : function(removeEl, keepListeners){
48543         if(this.loadMask){
48544             this.loadMask.destroy();
48545         }
48546         var c = this.container;
48547         c.removeAllListeners();
48548         this.view.destroy();
48549         this.colModel.purgeListeners();
48550         if(!keepListeners){
48551             this.purgeListeners();
48552         }
48553         c.update("");
48554         if(removeEl === true){
48555             c.remove();
48556         }
48557     },
48558
48559     // private
48560     processEvent : function(name, e){
48561         this.fireEvent(name, e);
48562         var t = e.getTarget();
48563         var v = this.view;
48564         var header = v.findHeaderIndex(t);
48565         if(header !== false){
48566             this.fireEvent("header" + name, this, header, e);
48567         }else{
48568             var row = v.findRowIndex(t);
48569             var cell = v.findCellIndex(t);
48570             if(row !== false){
48571                 this.fireEvent("row" + name, this, row, e);
48572                 if(cell !== false){
48573                     this.fireEvent("cell" + name, this, row, cell, e);
48574                 }
48575             }
48576         }
48577     },
48578
48579     // private
48580     onClick : function(e){
48581         this.processEvent("click", e);
48582     },
48583
48584     // private
48585     onContextMenu : function(e, t){
48586         this.processEvent("contextmenu", e);
48587     },
48588
48589     // private
48590     onDblClick : function(e){
48591         this.processEvent("dblclick", e);
48592     },
48593
48594     // private
48595     walkCells : function(row, col, step, fn, scope){
48596         var cm = this.colModel, clen = cm.getColumnCount();
48597         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48598         if(step < 0){
48599             if(col < 0){
48600                 row--;
48601                 first = false;
48602             }
48603             while(row >= 0){
48604                 if(!first){
48605                     col = clen-1;
48606                 }
48607                 first = false;
48608                 while(col >= 0){
48609                     if(fn.call(scope || this, row, col, cm) === true){
48610                         return [row, col];
48611                     }
48612                     col--;
48613                 }
48614                 row--;
48615             }
48616         } else {
48617             if(col >= clen){
48618                 row++;
48619                 first = false;
48620             }
48621             while(row < rlen){
48622                 if(!first){
48623                     col = 0;
48624                 }
48625                 first = false;
48626                 while(col < clen){
48627                     if(fn.call(scope || this, row, col, cm) === true){
48628                         return [row, col];
48629                     }
48630                     col++;
48631                 }
48632                 row++;
48633             }
48634         }
48635         return null;
48636     },
48637
48638     // private
48639     getSelections : function(){
48640         return this.selModel.getSelections();
48641     },
48642
48643     /**
48644      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48645      * but if manual update is required this method will initiate it.
48646      */
48647     autoSize : function(){
48648         if(this.rendered){
48649             this.view.layout();
48650             if(this.view.adjustForScroll){
48651                 this.view.adjustForScroll();
48652             }
48653         }
48654     },
48655
48656     /**
48657      * Returns the grid's underlying element.
48658      * @return {Element} The element
48659      */
48660     getGridEl : function(){
48661         return this.container;
48662     },
48663
48664     // private for compatibility, overridden by editor grid
48665     stopEditing : function(){},
48666
48667     /**
48668      * Returns the grid's SelectionModel.
48669      * @return {SelectionModel}
48670      */
48671     getSelectionModel : function(){
48672         if(!this.selModel){
48673             this.selModel = new Roo.grid.RowSelectionModel();
48674         }
48675         return this.selModel;
48676     },
48677
48678     /**
48679      * Returns the grid's DataSource.
48680      * @return {DataSource}
48681      */
48682     getDataSource : function(){
48683         return this.dataSource;
48684     },
48685
48686     /**
48687      * Returns the grid's ColumnModel.
48688      * @return {ColumnModel}
48689      */
48690     getColumnModel : function(){
48691         return this.colModel;
48692     },
48693
48694     /**
48695      * Returns the grid's GridView object.
48696      * @return {GridView}
48697      */
48698     getView : function(){
48699         if(!this.view){
48700             this.view = new Roo.grid.GridView(this.viewConfig);
48701         }
48702         return this.view;
48703     },
48704     /**
48705      * Called to get grid's drag proxy text, by default returns this.ddText.
48706      * @return {String}
48707      */
48708     getDragDropText : function(){
48709         var count = this.selModel.getCount();
48710         return String.format(this.ddText, count, count == 1 ? '' : 's');
48711     }
48712 });
48713 /**
48714  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48715  * %0 is replaced with the number of selected rows.
48716  * @type String
48717  */
48718 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48719  * Based on:
48720  * Ext JS Library 1.1.1
48721  * Copyright(c) 2006-2007, Ext JS, LLC.
48722  *
48723  * Originally Released Under LGPL - original licence link has changed is not relivant.
48724  *
48725  * Fork - LGPL
48726  * <script type="text/javascript">
48727  */
48728  
48729 Roo.grid.AbstractGridView = function(){
48730         this.grid = null;
48731         
48732         this.events = {
48733             "beforerowremoved" : true,
48734             "beforerowsinserted" : true,
48735             "beforerefresh" : true,
48736             "rowremoved" : true,
48737             "rowsinserted" : true,
48738             "rowupdated" : true,
48739             "refresh" : true
48740         };
48741     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48742 };
48743
48744 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48745     rowClass : "x-grid-row",
48746     cellClass : "x-grid-cell",
48747     tdClass : "x-grid-td",
48748     hdClass : "x-grid-hd",
48749     splitClass : "x-grid-hd-split",
48750     
48751         init: function(grid){
48752         this.grid = grid;
48753                 var cid = this.grid.getGridEl().id;
48754         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48755         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48756         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48757         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48758         },
48759         
48760         getColumnRenderers : function(){
48761         var renderers = [];
48762         var cm = this.grid.colModel;
48763         var colCount = cm.getColumnCount();
48764         for(var i = 0; i < colCount; i++){
48765             renderers[i] = cm.getRenderer(i);
48766         }
48767         return renderers;
48768     },
48769     
48770     getColumnIds : function(){
48771         var ids = [];
48772         var cm = this.grid.colModel;
48773         var colCount = cm.getColumnCount();
48774         for(var i = 0; i < colCount; i++){
48775             ids[i] = cm.getColumnId(i);
48776         }
48777         return ids;
48778     },
48779     
48780     getDataIndexes : function(){
48781         if(!this.indexMap){
48782             this.indexMap = this.buildIndexMap();
48783         }
48784         return this.indexMap.colToData;
48785     },
48786     
48787     getColumnIndexByDataIndex : function(dataIndex){
48788         if(!this.indexMap){
48789             this.indexMap = this.buildIndexMap();
48790         }
48791         return this.indexMap.dataToCol[dataIndex];
48792     },
48793     
48794     /**
48795      * Set a css style for a column dynamically. 
48796      * @param {Number} colIndex The index of the column
48797      * @param {String} name The css property name
48798      * @param {String} value The css value
48799      */
48800     setCSSStyle : function(colIndex, name, value){
48801         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48802         Roo.util.CSS.updateRule(selector, name, value);
48803     },
48804     
48805     generateRules : function(cm){
48806         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48807         Roo.util.CSS.removeStyleSheet(rulesId);
48808         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48809             var cid = cm.getColumnId(i);
48810             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48811                          this.tdSelector, cid, " {\n}\n",
48812                          this.hdSelector, cid, " {\n}\n",
48813                          this.splitSelector, cid, " {\n}\n");
48814         }
48815         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48816     }
48817 });/*
48818  * Based on:
48819  * Ext JS Library 1.1.1
48820  * Copyright(c) 2006-2007, Ext JS, LLC.
48821  *
48822  * Originally Released Under LGPL - original licence link has changed is not relivant.
48823  *
48824  * Fork - LGPL
48825  * <script type="text/javascript">
48826  */
48827
48828 // private
48829 // This is a support class used internally by the Grid components
48830 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48831     this.grid = grid;
48832     this.view = grid.getView();
48833     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48834     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48835     if(hd2){
48836         this.setHandleElId(Roo.id(hd));
48837         this.setOuterHandleElId(Roo.id(hd2));
48838     }
48839     this.scroll = false;
48840 };
48841 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48842     maxDragWidth: 120,
48843     getDragData : function(e){
48844         var t = Roo.lib.Event.getTarget(e);
48845         var h = this.view.findHeaderCell(t);
48846         if(h){
48847             return {ddel: h.firstChild, header:h};
48848         }
48849         return false;
48850     },
48851
48852     onInitDrag : function(e){
48853         this.view.headersDisabled = true;
48854         var clone = this.dragData.ddel.cloneNode(true);
48855         clone.id = Roo.id();
48856         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48857         this.proxy.update(clone);
48858         return true;
48859     },
48860
48861     afterValidDrop : function(){
48862         var v = this.view;
48863         setTimeout(function(){
48864             v.headersDisabled = false;
48865         }, 50);
48866     },
48867
48868     afterInvalidDrop : function(){
48869         var v = this.view;
48870         setTimeout(function(){
48871             v.headersDisabled = false;
48872         }, 50);
48873     }
48874 });
48875 /*
48876  * Based on:
48877  * Ext JS Library 1.1.1
48878  * Copyright(c) 2006-2007, Ext JS, LLC.
48879  *
48880  * Originally Released Under LGPL - original licence link has changed is not relivant.
48881  *
48882  * Fork - LGPL
48883  * <script type="text/javascript">
48884  */
48885 // private
48886 // This is a support class used internally by the Grid components
48887 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48888     this.grid = grid;
48889     this.view = grid.getView();
48890     // split the proxies so they don't interfere with mouse events
48891     this.proxyTop = Roo.DomHelper.append(document.body, {
48892         cls:"col-move-top", html:"&#160;"
48893     }, true);
48894     this.proxyBottom = Roo.DomHelper.append(document.body, {
48895         cls:"col-move-bottom", html:"&#160;"
48896     }, true);
48897     this.proxyTop.hide = this.proxyBottom.hide = function(){
48898         this.setLeftTop(-100,-100);
48899         this.setStyle("visibility", "hidden");
48900     };
48901     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48902     // temporarily disabled
48903     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48904     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48905 };
48906 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48907     proxyOffsets : [-4, -9],
48908     fly: Roo.Element.fly,
48909
48910     getTargetFromEvent : function(e){
48911         var t = Roo.lib.Event.getTarget(e);
48912         var cindex = this.view.findCellIndex(t);
48913         if(cindex !== false){
48914             return this.view.getHeaderCell(cindex);
48915         }
48916         return null;
48917     },
48918
48919     nextVisible : function(h){
48920         var v = this.view, cm = this.grid.colModel;
48921         h = h.nextSibling;
48922         while(h){
48923             if(!cm.isHidden(v.getCellIndex(h))){
48924                 return h;
48925             }
48926             h = h.nextSibling;
48927         }
48928         return null;
48929     },
48930
48931     prevVisible : function(h){
48932         var v = this.view, cm = this.grid.colModel;
48933         h = h.prevSibling;
48934         while(h){
48935             if(!cm.isHidden(v.getCellIndex(h))){
48936                 return h;
48937             }
48938             h = h.prevSibling;
48939         }
48940         return null;
48941     },
48942
48943     positionIndicator : function(h, n, e){
48944         var x = Roo.lib.Event.getPageX(e);
48945         var r = Roo.lib.Dom.getRegion(n.firstChild);
48946         var px, pt, py = r.top + this.proxyOffsets[1];
48947         if((r.right - x) <= (r.right-r.left)/2){
48948             px = r.right+this.view.borderWidth;
48949             pt = "after";
48950         }else{
48951             px = r.left;
48952             pt = "before";
48953         }
48954         var oldIndex = this.view.getCellIndex(h);
48955         var newIndex = this.view.getCellIndex(n);
48956
48957         if(this.grid.colModel.isFixed(newIndex)){
48958             return false;
48959         }
48960
48961         var locked = this.grid.colModel.isLocked(newIndex);
48962
48963         if(pt == "after"){
48964             newIndex++;
48965         }
48966         if(oldIndex < newIndex){
48967             newIndex--;
48968         }
48969         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48970             return false;
48971         }
48972         px +=  this.proxyOffsets[0];
48973         this.proxyTop.setLeftTop(px, py);
48974         this.proxyTop.show();
48975         if(!this.bottomOffset){
48976             this.bottomOffset = this.view.mainHd.getHeight();
48977         }
48978         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48979         this.proxyBottom.show();
48980         return pt;
48981     },
48982
48983     onNodeEnter : function(n, dd, e, data){
48984         if(data.header != n){
48985             this.positionIndicator(data.header, n, e);
48986         }
48987     },
48988
48989     onNodeOver : function(n, dd, e, data){
48990         var result = false;
48991         if(data.header != n){
48992             result = this.positionIndicator(data.header, n, e);
48993         }
48994         if(!result){
48995             this.proxyTop.hide();
48996             this.proxyBottom.hide();
48997         }
48998         return result ? this.dropAllowed : this.dropNotAllowed;
48999     },
49000
49001     onNodeOut : function(n, dd, e, data){
49002         this.proxyTop.hide();
49003         this.proxyBottom.hide();
49004     },
49005
49006     onNodeDrop : function(n, dd, e, data){
49007         var h = data.header;
49008         if(h != n){
49009             var cm = this.grid.colModel;
49010             var x = Roo.lib.Event.getPageX(e);
49011             var r = Roo.lib.Dom.getRegion(n.firstChild);
49012             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
49013             var oldIndex = this.view.getCellIndex(h);
49014             var newIndex = this.view.getCellIndex(n);
49015             var locked = cm.isLocked(newIndex);
49016             if(pt == "after"){
49017                 newIndex++;
49018             }
49019             if(oldIndex < newIndex){
49020                 newIndex--;
49021             }
49022             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
49023                 return false;
49024             }
49025             cm.setLocked(oldIndex, locked, true);
49026             cm.moveColumn(oldIndex, newIndex);
49027             this.grid.fireEvent("columnmove", oldIndex, newIndex);
49028             return true;
49029         }
49030         return false;
49031     }
49032 });
49033 /*
49034  * Based on:
49035  * Ext JS Library 1.1.1
49036  * Copyright(c) 2006-2007, Ext JS, LLC.
49037  *
49038  * Originally Released Under LGPL - original licence link has changed is not relivant.
49039  *
49040  * Fork - LGPL
49041  * <script type="text/javascript">
49042  */
49043   
49044 /**
49045  * @class Roo.grid.GridView
49046  * @extends Roo.util.Observable
49047  *
49048  * @constructor
49049  * @param {Object} config
49050  */
49051 Roo.grid.GridView = function(config){
49052     Roo.grid.GridView.superclass.constructor.call(this);
49053     this.el = null;
49054
49055     Roo.apply(this, config);
49056 };
49057
49058 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
49059
49060     
49061     rowClass : "x-grid-row",
49062
49063     cellClass : "x-grid-col",
49064
49065     tdClass : "x-grid-td",
49066
49067     hdClass : "x-grid-hd",
49068
49069     splitClass : "x-grid-split",
49070
49071     sortClasses : ["sort-asc", "sort-desc"],
49072
49073     enableMoveAnim : false,
49074
49075     hlColor: "C3DAF9",
49076
49077     dh : Roo.DomHelper,
49078
49079     fly : Roo.Element.fly,
49080
49081     css : Roo.util.CSS,
49082
49083     borderWidth: 1,
49084
49085     splitOffset: 3,
49086
49087     scrollIncrement : 22,
49088
49089     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
49090
49091     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
49092
49093     bind : function(ds, cm){
49094         if(this.ds){
49095             this.ds.un("load", this.onLoad, this);
49096             this.ds.un("datachanged", this.onDataChange, this);
49097             this.ds.un("add", this.onAdd, this);
49098             this.ds.un("remove", this.onRemove, this);
49099             this.ds.un("update", this.onUpdate, this);
49100             this.ds.un("clear", this.onClear, this);
49101         }
49102         if(ds){
49103             ds.on("load", this.onLoad, this);
49104             ds.on("datachanged", this.onDataChange, this);
49105             ds.on("add", this.onAdd, this);
49106             ds.on("remove", this.onRemove, this);
49107             ds.on("update", this.onUpdate, this);
49108             ds.on("clear", this.onClear, this);
49109         }
49110         this.ds = ds;
49111
49112         if(this.cm){
49113             this.cm.un("widthchange", this.onColWidthChange, this);
49114             this.cm.un("headerchange", this.onHeaderChange, this);
49115             this.cm.un("hiddenchange", this.onHiddenChange, this);
49116             this.cm.un("columnmoved", this.onColumnMove, this);
49117             this.cm.un("columnlockchange", this.onColumnLock, this);
49118         }
49119         if(cm){
49120             this.generateRules(cm);
49121             cm.on("widthchange", this.onColWidthChange, this);
49122             cm.on("headerchange", this.onHeaderChange, this);
49123             cm.on("hiddenchange", this.onHiddenChange, this);
49124             cm.on("columnmoved", this.onColumnMove, this);
49125             cm.on("columnlockchange", this.onColumnLock, this);
49126         }
49127         this.cm = cm;
49128     },
49129
49130     init: function(grid){
49131         Roo.grid.GridView.superclass.init.call(this, grid);
49132
49133         this.bind(grid.dataSource, grid.colModel);
49134
49135         grid.on("headerclick", this.handleHeaderClick, this);
49136
49137         if(grid.trackMouseOver){
49138             grid.on("mouseover", this.onRowOver, this);
49139             grid.on("mouseout", this.onRowOut, this);
49140         }
49141         grid.cancelTextSelection = function(){};
49142         this.gridId = grid.id;
49143
49144         var tpls = this.templates || {};
49145
49146         if(!tpls.master){
49147             tpls.master = new Roo.Template(
49148                '<div class="x-grid" hidefocus="true">',
49149                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
49150                   '<div class="x-grid-topbar"></div>',
49151                   '<div class="x-grid-scroller"><div></div></div>',
49152                   '<div class="x-grid-locked">',
49153                       '<div class="x-grid-header">{lockedHeader}</div>',
49154                       '<div class="x-grid-body">{lockedBody}</div>',
49155                   "</div>",
49156                   '<div class="x-grid-viewport">',
49157                       '<div class="x-grid-header">{header}</div>',
49158                       '<div class="x-grid-body">{body}</div>',
49159                   "</div>",
49160                   '<div class="x-grid-bottombar"></div>',
49161                  
49162                   '<div class="x-grid-resize-proxy">&#160;</div>',
49163                "</div>"
49164             );
49165             tpls.master.disableformats = true;
49166         }
49167
49168         if(!tpls.header){
49169             tpls.header = new Roo.Template(
49170                '<table border="0" cellspacing="0" cellpadding="0">',
49171                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
49172                "</table>{splits}"
49173             );
49174             tpls.header.disableformats = true;
49175         }
49176         tpls.header.compile();
49177
49178         if(!tpls.hcell){
49179             tpls.hcell = new Roo.Template(
49180                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
49181                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
49182                 "</div></td>"
49183              );
49184              tpls.hcell.disableFormats = true;
49185         }
49186         tpls.hcell.compile();
49187
49188         if(!tpls.hsplit){
49189             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
49190             tpls.hsplit.disableFormats = true;
49191         }
49192         tpls.hsplit.compile();
49193
49194         if(!tpls.body){
49195             tpls.body = new Roo.Template(
49196                '<table border="0" cellspacing="0" cellpadding="0">',
49197                "<tbody>{rows}</tbody>",
49198                "</table>"
49199             );
49200             tpls.body.disableFormats = true;
49201         }
49202         tpls.body.compile();
49203
49204         if(!tpls.row){
49205             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
49206             tpls.row.disableFormats = true;
49207         }
49208         tpls.row.compile();
49209
49210         if(!tpls.cell){
49211             tpls.cell = new Roo.Template(
49212                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
49213                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
49214                 "</td>"
49215             );
49216             tpls.cell.disableFormats = true;
49217         }
49218         tpls.cell.compile();
49219
49220         this.templates = tpls;
49221     },
49222
49223     // remap these for backwards compat
49224     onColWidthChange : function(){
49225         this.updateColumns.apply(this, arguments);
49226     },
49227     onHeaderChange : function(){
49228         this.updateHeaders.apply(this, arguments);
49229     }, 
49230     onHiddenChange : function(){
49231         this.handleHiddenChange.apply(this, arguments);
49232     },
49233     onColumnMove : function(){
49234         this.handleColumnMove.apply(this, arguments);
49235     },
49236     onColumnLock : function(){
49237         this.handleLockChange.apply(this, arguments);
49238     },
49239
49240     onDataChange : function(){
49241         this.refresh();
49242         this.updateHeaderSortState();
49243     },
49244
49245     onClear : function(){
49246         this.refresh();
49247     },
49248
49249     onUpdate : function(ds, record){
49250         this.refreshRow(record);
49251     },
49252
49253     refreshRow : function(record){
49254         var ds = this.ds, index;
49255         if(typeof record == 'number'){
49256             index = record;
49257             record = ds.getAt(index);
49258         }else{
49259             index = ds.indexOf(record);
49260         }
49261         this.insertRows(ds, index, index, true);
49262         this.onRemove(ds, record, index+1, true);
49263         this.syncRowHeights(index, index);
49264         this.layout();
49265         this.fireEvent("rowupdated", this, index, record);
49266     },
49267
49268     onAdd : function(ds, records, index){
49269         this.insertRows(ds, index, index + (records.length-1));
49270     },
49271
49272     onRemove : function(ds, record, index, isUpdate){
49273         if(isUpdate !== true){
49274             this.fireEvent("beforerowremoved", this, index, record);
49275         }
49276         var bt = this.getBodyTable(), lt = this.getLockedTable();
49277         if(bt.rows[index]){
49278             bt.firstChild.removeChild(bt.rows[index]);
49279         }
49280         if(lt.rows[index]){
49281             lt.firstChild.removeChild(lt.rows[index]);
49282         }
49283         if(isUpdate !== true){
49284             this.stripeRows(index);
49285             this.syncRowHeights(index, index);
49286             this.layout();
49287             this.fireEvent("rowremoved", this, index, record);
49288         }
49289     },
49290
49291     onLoad : function(){
49292         this.scrollToTop();
49293     },
49294
49295     /**
49296      * Scrolls the grid to the top
49297      */
49298     scrollToTop : function(){
49299         if(this.scroller){
49300             this.scroller.dom.scrollTop = 0;
49301             this.syncScroll();
49302         }
49303     },
49304
49305     /**
49306      * Gets a panel in the header of the grid that can be used for toolbars etc.
49307      * After modifying the contents of this panel a call to grid.autoSize() may be
49308      * required to register any changes in size.
49309      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
49310      * @return Roo.Element
49311      */
49312     getHeaderPanel : function(doShow){
49313         if(doShow){
49314             this.headerPanel.show();
49315         }
49316         return this.headerPanel;
49317     },
49318
49319     /**
49320      * Gets a panel in the footer of the grid that can be used for toolbars etc.
49321      * After modifying the contents of this panel a call to grid.autoSize() may be
49322      * required to register any changes in size.
49323      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
49324      * @return Roo.Element
49325      */
49326     getFooterPanel : function(doShow){
49327         if(doShow){
49328             this.footerPanel.show();
49329         }
49330         return this.footerPanel;
49331     },
49332
49333     initElements : function(){
49334         var E = Roo.Element;
49335         var el = this.grid.getGridEl().dom.firstChild;
49336         var cs = el.childNodes;
49337
49338         this.el = new E(el);
49339         
49340          this.focusEl = new E(el.firstChild);
49341         this.focusEl.swallowEvent("click", true);
49342         
49343         this.headerPanel = new E(cs[1]);
49344         this.headerPanel.enableDisplayMode("block");
49345
49346         this.scroller = new E(cs[2]);
49347         this.scrollSizer = new E(this.scroller.dom.firstChild);
49348
49349         this.lockedWrap = new E(cs[3]);
49350         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
49351         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
49352
49353         this.mainWrap = new E(cs[4]);
49354         this.mainHd = new E(this.mainWrap.dom.firstChild);
49355         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
49356
49357         this.footerPanel = new E(cs[5]);
49358         this.footerPanel.enableDisplayMode("block");
49359
49360         this.resizeProxy = new E(cs[6]);
49361
49362         this.headerSelector = String.format(
49363            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
49364            this.lockedHd.id, this.mainHd.id
49365         );
49366
49367         this.splitterSelector = String.format(
49368            '#{0} div.x-grid-split, #{1} div.x-grid-split',
49369            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
49370         );
49371     },
49372     idToCssName : function(s)
49373     {
49374         return s.replace(/[^a-z0-9]+/ig, '-');
49375     },
49376
49377     getHeaderCell : function(index){
49378         return Roo.DomQuery.select(this.headerSelector)[index];
49379     },
49380
49381     getHeaderCellMeasure : function(index){
49382         return this.getHeaderCell(index).firstChild;
49383     },
49384
49385     getHeaderCellText : function(index){
49386         return this.getHeaderCell(index).firstChild.firstChild;
49387     },
49388
49389     getLockedTable : function(){
49390         return this.lockedBody.dom.firstChild;
49391     },
49392
49393     getBodyTable : function(){
49394         return this.mainBody.dom.firstChild;
49395     },
49396
49397     getLockedRow : function(index){
49398         return this.getLockedTable().rows[index];
49399     },
49400
49401     getRow : function(index){
49402         return this.getBodyTable().rows[index];
49403     },
49404
49405     getRowComposite : function(index){
49406         if(!this.rowEl){
49407             this.rowEl = new Roo.CompositeElementLite();
49408         }
49409         var els = [], lrow, mrow;
49410         if(lrow = this.getLockedRow(index)){
49411             els.push(lrow);
49412         }
49413         if(mrow = this.getRow(index)){
49414             els.push(mrow);
49415         }
49416         this.rowEl.elements = els;
49417         return this.rowEl;
49418     },
49419     /**
49420      * Gets the 'td' of the cell
49421      * 
49422      * @param {Integer} rowIndex row to select
49423      * @param {Integer} colIndex column to select
49424      * 
49425      * @return {Object} 
49426      */
49427     getCell : function(rowIndex, colIndex){
49428         var locked = this.cm.getLockedCount();
49429         var source;
49430         if(colIndex < locked){
49431             source = this.lockedBody.dom.firstChild;
49432         }else{
49433             source = this.mainBody.dom.firstChild;
49434             colIndex -= locked;
49435         }
49436         return source.rows[rowIndex].childNodes[colIndex];
49437     },
49438
49439     getCellText : function(rowIndex, colIndex){
49440         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
49441     },
49442
49443     getCellBox : function(cell){
49444         var b = this.fly(cell).getBox();
49445         if(Roo.isOpera){ // opera fails to report the Y
49446             b.y = cell.offsetTop + this.mainBody.getY();
49447         }
49448         return b;
49449     },
49450
49451     getCellIndex : function(cell){
49452         var id = String(cell.className).match(this.cellRE);
49453         if(id){
49454             return parseInt(id[1], 10);
49455         }
49456         return 0;
49457     },
49458
49459     findHeaderIndex : function(n){
49460         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49461         return r ? this.getCellIndex(r) : false;
49462     },
49463
49464     findHeaderCell : function(n){
49465         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
49466         return r ? r : false;
49467     },
49468
49469     findRowIndex : function(n){
49470         if(!n){
49471             return false;
49472         }
49473         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
49474         return r ? r.rowIndex : false;
49475     },
49476
49477     findCellIndex : function(node){
49478         var stop = this.el.dom;
49479         while(node && node != stop){
49480             if(this.findRE.test(node.className)){
49481                 return this.getCellIndex(node);
49482             }
49483             node = node.parentNode;
49484         }
49485         return false;
49486     },
49487
49488     getColumnId : function(index){
49489         return this.cm.getColumnId(index);
49490     },
49491
49492     getSplitters : function()
49493     {
49494         if(this.splitterSelector){
49495            return Roo.DomQuery.select(this.splitterSelector);
49496         }else{
49497             return null;
49498       }
49499     },
49500
49501     getSplitter : function(index){
49502         return this.getSplitters()[index];
49503     },
49504
49505     onRowOver : function(e, t){
49506         var row;
49507         if((row = this.findRowIndex(t)) !== false){
49508             this.getRowComposite(row).addClass("x-grid-row-over");
49509         }
49510     },
49511
49512     onRowOut : function(e, t){
49513         var row;
49514         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
49515             this.getRowComposite(row).removeClass("x-grid-row-over");
49516         }
49517     },
49518
49519     renderHeaders : function(){
49520         var cm = this.cm;
49521         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
49522         var cb = [], lb = [], sb = [], lsb = [], p = {};
49523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49524             p.cellId = "x-grid-hd-0-" + i;
49525             p.splitId = "x-grid-csplit-0-" + i;
49526             p.id = cm.getColumnId(i);
49527             p.title = cm.getColumnTooltip(i) || "";
49528             p.value = cm.getColumnHeader(i) || "";
49529             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
49530             if(!cm.isLocked(i)){
49531                 cb[cb.length] = ct.apply(p);
49532                 sb[sb.length] = st.apply(p);
49533             }else{
49534                 lb[lb.length] = ct.apply(p);
49535                 lsb[lsb.length] = st.apply(p);
49536             }
49537         }
49538         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
49539                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
49540     },
49541
49542     updateHeaders : function(){
49543         var html = this.renderHeaders();
49544         this.lockedHd.update(html[0]);
49545         this.mainHd.update(html[1]);
49546     },
49547
49548     /**
49549      * Focuses the specified row.
49550      * @param {Number} row The row index
49551      */
49552     focusRow : function(row)
49553     {
49554         //Roo.log('GridView.focusRow');
49555         var x = this.scroller.dom.scrollLeft;
49556         this.focusCell(row, 0, false);
49557         this.scroller.dom.scrollLeft = x;
49558     },
49559
49560     /**
49561      * Focuses the specified cell.
49562      * @param {Number} row The row index
49563      * @param {Number} col The column index
49564      * @param {Boolean} hscroll false to disable horizontal scrolling
49565      */
49566     focusCell : function(row, col, hscroll)
49567     {
49568         //Roo.log('GridView.focusCell');
49569         var el = this.ensureVisible(row, col, hscroll);
49570         this.focusEl.alignTo(el, "tl-tl");
49571         if(Roo.isGecko){
49572             this.focusEl.focus();
49573         }else{
49574             this.focusEl.focus.defer(1, this.focusEl);
49575         }
49576     },
49577
49578     /**
49579      * Scrolls the specified cell into view
49580      * @param {Number} row The row index
49581      * @param {Number} col The column index
49582      * @param {Boolean} hscroll false to disable horizontal scrolling
49583      */
49584     ensureVisible : function(row, col, hscroll)
49585     {
49586         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49587         //return null; //disable for testing.
49588         if(typeof row != "number"){
49589             row = row.rowIndex;
49590         }
49591         if(row < 0 && row >= this.ds.getCount()){
49592             return  null;
49593         }
49594         col = (col !== undefined ? col : 0);
49595         var cm = this.grid.colModel;
49596         while(cm.isHidden(col)){
49597             col++;
49598         }
49599
49600         var el = this.getCell(row, col);
49601         if(!el){
49602             return null;
49603         }
49604         var c = this.scroller.dom;
49605
49606         var ctop = parseInt(el.offsetTop, 10);
49607         var cleft = parseInt(el.offsetLeft, 10);
49608         var cbot = ctop + el.offsetHeight;
49609         var cright = cleft + el.offsetWidth;
49610         
49611         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49612         var stop = parseInt(c.scrollTop, 10);
49613         var sleft = parseInt(c.scrollLeft, 10);
49614         var sbot = stop + ch;
49615         var sright = sleft + c.clientWidth;
49616         /*
49617         Roo.log('GridView.ensureVisible:' +
49618                 ' ctop:' + ctop +
49619                 ' c.clientHeight:' + c.clientHeight +
49620                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49621                 ' stop:' + stop +
49622                 ' cbot:' + cbot +
49623                 ' sbot:' + sbot +
49624                 ' ch:' + ch  
49625                 );
49626         */
49627         if(ctop < stop){
49628              c.scrollTop = ctop;
49629             //Roo.log("set scrolltop to ctop DISABLE?");
49630         }else if(cbot > sbot){
49631             //Roo.log("set scrolltop to cbot-ch");
49632             c.scrollTop = cbot-ch;
49633         }
49634         
49635         if(hscroll !== false){
49636             if(cleft < sleft){
49637                 c.scrollLeft = cleft;
49638             }else if(cright > sright){
49639                 c.scrollLeft = cright-c.clientWidth;
49640             }
49641         }
49642          
49643         return el;
49644     },
49645
49646     updateColumns : function(){
49647         this.grid.stopEditing();
49648         var cm = this.grid.colModel, colIds = this.getColumnIds();
49649         //var totalWidth = cm.getTotalWidth();
49650         var pos = 0;
49651         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49652             //if(cm.isHidden(i)) continue;
49653             var w = cm.getColumnWidth(i);
49654             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49655             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49656         }
49657         this.updateSplitters();
49658     },
49659
49660     generateRules : function(cm){
49661         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49662         Roo.util.CSS.removeStyleSheet(rulesId);
49663         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49664             var cid = cm.getColumnId(i);
49665             var align = '';
49666             if(cm.config[i].align){
49667                 align = 'text-align:'+cm.config[i].align+';';
49668             }
49669             var hidden = '';
49670             if(cm.isHidden(i)){
49671                 hidden = 'display:none;';
49672             }
49673             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49674             ruleBuf.push(
49675                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49676                     this.hdSelector, cid, " {\n", align, width, "}\n",
49677                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49678                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49679         }
49680         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49681     },
49682
49683     updateSplitters : function(){
49684         var cm = this.cm, s = this.getSplitters();
49685         if(s){ // splitters not created yet
49686             var pos = 0, locked = true;
49687             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49688                 if(cm.isHidden(i)) continue;
49689                 var w = cm.getColumnWidth(i); // make sure it's a number
49690                 if(!cm.isLocked(i) && locked){
49691                     pos = 0;
49692                     locked = false;
49693                 }
49694                 pos += w;
49695                 s[i].style.left = (pos-this.splitOffset) + "px";
49696             }
49697         }
49698     },
49699
49700     handleHiddenChange : function(colModel, colIndex, hidden){
49701         if(hidden){
49702             this.hideColumn(colIndex);
49703         }else{
49704             this.unhideColumn(colIndex);
49705         }
49706     },
49707
49708     hideColumn : function(colIndex){
49709         var cid = this.getColumnId(colIndex);
49710         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49711         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49712         if(Roo.isSafari){
49713             this.updateHeaders();
49714         }
49715         this.updateSplitters();
49716         this.layout();
49717     },
49718
49719     unhideColumn : function(colIndex){
49720         var cid = this.getColumnId(colIndex);
49721         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49722         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49723
49724         if(Roo.isSafari){
49725             this.updateHeaders();
49726         }
49727         this.updateSplitters();
49728         this.layout();
49729     },
49730
49731     insertRows : function(dm, firstRow, lastRow, isUpdate){
49732         if(firstRow == 0 && lastRow == dm.getCount()-1){
49733             this.refresh();
49734         }else{
49735             if(!isUpdate){
49736                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49737             }
49738             var s = this.getScrollState();
49739             var markup = this.renderRows(firstRow, lastRow);
49740             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49741             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49742             this.restoreScroll(s);
49743             if(!isUpdate){
49744                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49745                 this.syncRowHeights(firstRow, lastRow);
49746                 this.stripeRows(firstRow);
49747                 this.layout();
49748             }
49749         }
49750     },
49751
49752     bufferRows : function(markup, target, index){
49753         var before = null, trows = target.rows, tbody = target.tBodies[0];
49754         if(index < trows.length){
49755             before = trows[index];
49756         }
49757         var b = document.createElement("div");
49758         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49759         var rows = b.firstChild.rows;
49760         for(var i = 0, len = rows.length; i < len; i++){
49761             if(before){
49762                 tbody.insertBefore(rows[0], before);
49763             }else{
49764                 tbody.appendChild(rows[0]);
49765             }
49766         }
49767         b.innerHTML = "";
49768         b = null;
49769     },
49770
49771     deleteRows : function(dm, firstRow, lastRow){
49772         if(dm.getRowCount()<1){
49773             this.fireEvent("beforerefresh", this);
49774             this.mainBody.update("");
49775             this.lockedBody.update("");
49776             this.fireEvent("refresh", this);
49777         }else{
49778             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49779             var bt = this.getBodyTable();
49780             var tbody = bt.firstChild;
49781             var rows = bt.rows;
49782             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49783                 tbody.removeChild(rows[firstRow]);
49784             }
49785             this.stripeRows(firstRow);
49786             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49787         }
49788     },
49789
49790     updateRows : function(dataSource, firstRow, lastRow){
49791         var s = this.getScrollState();
49792         this.refresh();
49793         this.restoreScroll(s);
49794     },
49795
49796     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49797         if(!noRefresh){
49798            this.refresh();
49799         }
49800         this.updateHeaderSortState();
49801     },
49802
49803     getScrollState : function(){
49804         
49805         var sb = this.scroller.dom;
49806         return {left: sb.scrollLeft, top: sb.scrollTop};
49807     },
49808
49809     stripeRows : function(startRow){
49810         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49811             return;
49812         }
49813         startRow = startRow || 0;
49814         var rows = this.getBodyTable().rows;
49815         var lrows = this.getLockedTable().rows;
49816         var cls = ' x-grid-row-alt ';
49817         for(var i = startRow, len = rows.length; i < len; i++){
49818             var row = rows[i], lrow = lrows[i];
49819             var isAlt = ((i+1) % 2 == 0);
49820             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49821             if(isAlt == hasAlt){
49822                 continue;
49823             }
49824             if(isAlt){
49825                 row.className += " x-grid-row-alt";
49826             }else{
49827                 row.className = row.className.replace("x-grid-row-alt", "");
49828             }
49829             if(lrow){
49830                 lrow.className = row.className;
49831             }
49832         }
49833     },
49834
49835     restoreScroll : function(state){
49836         //Roo.log('GridView.restoreScroll');
49837         var sb = this.scroller.dom;
49838         sb.scrollLeft = state.left;
49839         sb.scrollTop = state.top;
49840         this.syncScroll();
49841     },
49842
49843     syncScroll : function(){
49844         //Roo.log('GridView.syncScroll');
49845         var sb = this.scroller.dom;
49846         var sh = this.mainHd.dom;
49847         var bs = this.mainBody.dom;
49848         var lv = this.lockedBody.dom;
49849         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49850         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49851     },
49852
49853     handleScroll : function(e){
49854         this.syncScroll();
49855         var sb = this.scroller.dom;
49856         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49857         e.stopEvent();
49858     },
49859
49860     handleWheel : function(e){
49861         var d = e.getWheelDelta();
49862         this.scroller.dom.scrollTop -= d*22;
49863         // set this here to prevent jumpy scrolling on large tables
49864         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49865         e.stopEvent();
49866     },
49867
49868     renderRows : function(startRow, endRow){
49869         // pull in all the crap needed to render rows
49870         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49871         var colCount = cm.getColumnCount();
49872
49873         if(ds.getCount() < 1){
49874             return ["", ""];
49875         }
49876
49877         // build a map for all the columns
49878         var cs = [];
49879         for(var i = 0; i < colCount; i++){
49880             var name = cm.getDataIndex(i);
49881             cs[i] = {
49882                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49883                 renderer : cm.getRenderer(i),
49884                 id : cm.getColumnId(i),
49885                 locked : cm.isLocked(i)
49886             };
49887         }
49888
49889         startRow = startRow || 0;
49890         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49891
49892         // records to render
49893         var rs = ds.getRange(startRow, endRow);
49894
49895         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49896     },
49897
49898     // As much as I hate to duplicate code, this was branched because FireFox really hates
49899     // [].join("") on strings. The performance difference was substantial enough to
49900     // branch this function
49901     doRender : Roo.isGecko ?
49902             function(cs, rs, ds, startRow, colCount, stripe){
49903                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49904                 // buffers
49905                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49906                 
49907                 var hasListener = this.grid.hasListener('rowclass');
49908                 var rowcfg = {};
49909                 for(var j = 0, len = rs.length; j < len; j++){
49910                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49911                     for(var i = 0; i < colCount; i++){
49912                         c = cs[i];
49913                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49914                         p.id = c.id;
49915                         p.css = p.attr = "";
49916                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49917                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49918                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49919                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49920                         }
49921                         var markup = ct.apply(p);
49922                         if(!c.locked){
49923                             cb+= markup;
49924                         }else{
49925                             lcb+= markup;
49926                         }
49927                     }
49928                     var alt = [];
49929                     if(stripe && ((rowIndex+1) % 2 == 0)){
49930                         alt.push("x-grid-row-alt")
49931                     }
49932                     if(r.dirty){
49933                         alt.push(  " x-grid-dirty-row");
49934                     }
49935                     rp.cells = lcb;
49936                     if(this.getRowClass){
49937                         alt.push(this.getRowClass(r, rowIndex));
49938                     }
49939                     if (hasListener) {
49940                         rowcfg = {
49941                              
49942                             record: r,
49943                             rowIndex : rowIndex,
49944                             rowClass : ''
49945                         }
49946                         this.grid.fireEvent('rowclass', this, rowcfg);
49947                         alt.push(rowcfg.rowClass);
49948                     }
49949                     rp.alt = alt.join(" ");
49950                     lbuf+= rt.apply(rp);
49951                     rp.cells = cb;
49952                     buf+=  rt.apply(rp);
49953                 }
49954                 return [lbuf, buf];
49955             } :
49956             function(cs, rs, ds, startRow, colCount, stripe){
49957                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49958                 // buffers
49959                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49960                 var hasListener = this.grid.hasListener('rowclass');
49961  
49962                 var rowcfg = {};
49963                 for(var j = 0, len = rs.length; j < len; j++){
49964                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49965                     for(var i = 0; i < colCount; i++){
49966                         c = cs[i];
49967                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49968                         p.id = c.id;
49969                         p.css = p.attr = "";
49970                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49971                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49972                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49973                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49974                         }
49975                         
49976                         var markup = ct.apply(p);
49977                         if(!c.locked){
49978                             cb[cb.length] = markup;
49979                         }else{
49980                             lcb[lcb.length] = markup;
49981                         }
49982                     }
49983                     var alt = [];
49984                     if(stripe && ((rowIndex+1) % 2 == 0)){
49985                         alt.push( "x-grid-row-alt");
49986                     }
49987                     if(r.dirty){
49988                         alt.push(" x-grid-dirty-row");
49989                     }
49990                     rp.cells = lcb;
49991                     if(this.getRowClass){
49992                         alt.push( this.getRowClass(r, rowIndex));
49993                     }
49994                     if (hasListener) {
49995                         rowcfg = {
49996                              
49997                             record: r,
49998                             rowIndex : rowIndex,
49999                             rowClass : ''
50000                         }
50001                         this.grid.fireEvent('rowclass', this, rowcfg);
50002                         alt.push(rowcfg.rowClass);
50003                     }
50004                     rp.alt = alt.join(" ");
50005                     rp.cells = lcb.join("");
50006                     lbuf[lbuf.length] = rt.apply(rp);
50007                     rp.cells = cb.join("");
50008                     buf[buf.length] =  rt.apply(rp);
50009                 }
50010                 return [lbuf.join(""), buf.join("")];
50011             },
50012
50013     renderBody : function(){
50014         var markup = this.renderRows();
50015         var bt = this.templates.body;
50016         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
50017     },
50018
50019     /**
50020      * Refreshes the grid
50021      * @param {Boolean} headersToo
50022      */
50023     refresh : function(headersToo){
50024         this.fireEvent("beforerefresh", this);
50025         this.grid.stopEditing();
50026         var result = this.renderBody();
50027         this.lockedBody.update(result[0]);
50028         this.mainBody.update(result[1]);
50029         if(headersToo === true){
50030             this.updateHeaders();
50031             this.updateColumns();
50032             this.updateSplitters();
50033             this.updateHeaderSortState();
50034         }
50035         this.syncRowHeights();
50036         this.layout();
50037         this.fireEvent("refresh", this);
50038     },
50039
50040     handleColumnMove : function(cm, oldIndex, newIndex){
50041         this.indexMap = null;
50042         var s = this.getScrollState();
50043         this.refresh(true);
50044         this.restoreScroll(s);
50045         this.afterMove(newIndex);
50046     },
50047
50048     afterMove : function(colIndex){
50049         if(this.enableMoveAnim && Roo.enableFx){
50050             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
50051         }
50052         // if multisort - fix sortOrder, and reload..
50053         if (this.grid.dataSource.multiSort) {
50054             // the we can call sort again..
50055             var dm = this.grid.dataSource;
50056             var cm = this.grid.colModel;
50057             var so = [];
50058             for(var i = 0; i < cm.config.length; i++ ) {
50059                 
50060                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
50061                     continue; // dont' bother, it's not in sort list or being set.
50062                 }
50063                 
50064                 so.push(cm.config[i].dataIndex);
50065             };
50066             dm.sortOrder = so;
50067             dm.load(dm.lastOptions);
50068             
50069             
50070         }
50071         
50072     },
50073
50074     updateCell : function(dm, rowIndex, dataIndex){
50075         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
50076         if(typeof colIndex == "undefined"){ // not present in grid
50077             return;
50078         }
50079         var cm = this.grid.colModel;
50080         var cell = this.getCell(rowIndex, colIndex);
50081         var cellText = this.getCellText(rowIndex, colIndex);
50082
50083         var p = {
50084             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
50085             id : cm.getColumnId(colIndex),
50086             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
50087         };
50088         var renderer = cm.getRenderer(colIndex);
50089         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
50090         if(typeof val == "undefined" || val === "") val = "&#160;";
50091         cellText.innerHTML = val;
50092         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
50093         this.syncRowHeights(rowIndex, rowIndex);
50094     },
50095
50096     calcColumnWidth : function(colIndex, maxRowsToMeasure){
50097         var maxWidth = 0;
50098         if(this.grid.autoSizeHeaders){
50099             var h = this.getHeaderCellMeasure(colIndex);
50100             maxWidth = Math.max(maxWidth, h.scrollWidth);
50101         }
50102         var tb, index;
50103         if(this.cm.isLocked(colIndex)){
50104             tb = this.getLockedTable();
50105             index = colIndex;
50106         }else{
50107             tb = this.getBodyTable();
50108             index = colIndex - this.cm.getLockedCount();
50109         }
50110         if(tb && tb.rows){
50111             var rows = tb.rows;
50112             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
50113             for(var i = 0; i < stopIndex; i++){
50114                 var cell = rows[i].childNodes[index].firstChild;
50115                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
50116             }
50117         }
50118         return maxWidth + /*margin for error in IE*/ 5;
50119     },
50120     /**
50121      * Autofit a column to its content.
50122      * @param {Number} colIndex
50123      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
50124      */
50125      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
50126          if(this.cm.isHidden(colIndex)){
50127              return; // can't calc a hidden column
50128          }
50129         if(forceMinSize){
50130             var cid = this.cm.getColumnId(colIndex);
50131             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
50132            if(this.grid.autoSizeHeaders){
50133                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
50134            }
50135         }
50136         var newWidth = this.calcColumnWidth(colIndex);
50137         this.cm.setColumnWidth(colIndex,
50138             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
50139         if(!suppressEvent){
50140             this.grid.fireEvent("columnresize", colIndex, newWidth);
50141         }
50142     },
50143
50144     /**
50145      * Autofits all columns to their content and then expands to fit any extra space in the grid
50146      */
50147      autoSizeColumns : function(){
50148         var cm = this.grid.colModel;
50149         var colCount = cm.getColumnCount();
50150         for(var i = 0; i < colCount; i++){
50151             this.autoSizeColumn(i, true, true);
50152         }
50153         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
50154             this.fitColumns();
50155         }else{
50156             this.updateColumns();
50157             this.layout();
50158         }
50159     },
50160
50161     /**
50162      * Autofits all columns to the grid's width proportionate with their current size
50163      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
50164      */
50165     fitColumns : function(reserveScrollSpace){
50166         var cm = this.grid.colModel;
50167         var colCount = cm.getColumnCount();
50168         var cols = [];
50169         var width = 0;
50170         var i, w;
50171         for (i = 0; i < colCount; i++){
50172             if(!cm.isHidden(i) && !cm.isFixed(i)){
50173                 w = cm.getColumnWidth(i);
50174                 cols.push(i);
50175                 cols.push(w);
50176                 width += w;
50177             }
50178         }
50179         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
50180         if(reserveScrollSpace){
50181             avail -= 17;
50182         }
50183         var frac = (avail - cm.getTotalWidth())/width;
50184         while (cols.length){
50185             w = cols.pop();
50186             i = cols.pop();
50187             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
50188         }
50189         this.updateColumns();
50190         this.layout();
50191     },
50192
50193     onRowSelect : function(rowIndex){
50194         var row = this.getRowComposite(rowIndex);
50195         row.addClass("x-grid-row-selected");
50196     },
50197
50198     onRowDeselect : function(rowIndex){
50199         var row = this.getRowComposite(rowIndex);
50200         row.removeClass("x-grid-row-selected");
50201     },
50202
50203     onCellSelect : function(row, col){
50204         var cell = this.getCell(row, col);
50205         if(cell){
50206             Roo.fly(cell).addClass("x-grid-cell-selected");
50207         }
50208     },
50209
50210     onCellDeselect : function(row, col){
50211         var cell = this.getCell(row, col);
50212         if(cell){
50213             Roo.fly(cell).removeClass("x-grid-cell-selected");
50214         }
50215     },
50216
50217     updateHeaderSortState : function(){
50218         
50219         // sort state can be single { field: xxx, direction : yyy}
50220         // or   { xxx=>ASC , yyy : DESC ..... }
50221         
50222         var mstate = {};
50223         if (!this.ds.multiSort) { 
50224             var state = this.ds.getSortState();
50225             if(!state){
50226                 return;
50227             }
50228             mstate[state.field] = state.direction;
50229             // FIXME... - this is not used here.. but might be elsewhere..
50230             this.sortState = state;
50231             
50232         } else {
50233             mstate = this.ds.sortToggle;
50234         }
50235         //remove existing sort classes..
50236         
50237         var sc = this.sortClasses;
50238         var hds = this.el.select(this.headerSelector).removeClass(sc);
50239         
50240         for(var f in mstate) {
50241         
50242             var sortColumn = this.cm.findColumnIndex(f);
50243             
50244             if(sortColumn != -1){
50245                 var sortDir = mstate[f];        
50246                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
50247             }
50248         }
50249         
50250          
50251         
50252     },
50253
50254
50255     handleHeaderClick : function(g, index){
50256         if(this.headersDisabled){
50257             return;
50258         }
50259         var dm = g.dataSource, cm = g.colModel;
50260         if(!cm.isSortable(index)){
50261             return;
50262         }
50263         g.stopEditing();
50264         
50265         if (dm.multiSort) {
50266             // update the sortOrder
50267             var so = [];
50268             for(var i = 0; i < cm.config.length; i++ ) {
50269                 
50270                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
50271                     continue; // dont' bother, it's not in sort list or being set.
50272                 }
50273                 
50274                 so.push(cm.config[i].dataIndex);
50275             };
50276             dm.sortOrder = so;
50277         }
50278         
50279         
50280         dm.sort(cm.getDataIndex(index));
50281     },
50282
50283
50284     destroy : function(){
50285         if(this.colMenu){
50286             this.colMenu.removeAll();
50287             Roo.menu.MenuMgr.unregister(this.colMenu);
50288             this.colMenu.getEl().remove();
50289             delete this.colMenu;
50290         }
50291         if(this.hmenu){
50292             this.hmenu.removeAll();
50293             Roo.menu.MenuMgr.unregister(this.hmenu);
50294             this.hmenu.getEl().remove();
50295             delete this.hmenu;
50296         }
50297         if(this.grid.enableColumnMove){
50298             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50299             if(dds){
50300                 for(var dd in dds){
50301                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
50302                         var elid = dds[dd].dragElId;
50303                         dds[dd].unreg();
50304                         Roo.get(elid).remove();
50305                     } else if(dds[dd].config.isTarget){
50306                         dds[dd].proxyTop.remove();
50307                         dds[dd].proxyBottom.remove();
50308                         dds[dd].unreg();
50309                     }
50310                     if(Roo.dd.DDM.locationCache[dd]){
50311                         delete Roo.dd.DDM.locationCache[dd];
50312                     }
50313                 }
50314                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
50315             }
50316         }
50317         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
50318         this.bind(null, null);
50319         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
50320     },
50321
50322     handleLockChange : function(){
50323         this.refresh(true);
50324     },
50325
50326     onDenyColumnLock : function(){
50327
50328     },
50329
50330     onDenyColumnHide : function(){
50331
50332     },
50333
50334     handleHdMenuClick : function(item){
50335         var index = this.hdCtxIndex;
50336         var cm = this.cm, ds = this.ds;
50337         switch(item.id){
50338             case "asc":
50339                 ds.sort(cm.getDataIndex(index), "ASC");
50340                 break;
50341             case "desc":
50342                 ds.sort(cm.getDataIndex(index), "DESC");
50343                 break;
50344             case "lock":
50345                 var lc = cm.getLockedCount();
50346                 if(cm.getColumnCount(true) <= lc+1){
50347                     this.onDenyColumnLock();
50348                     return;
50349                 }
50350                 if(lc != index){
50351                     cm.setLocked(index, true, true);
50352                     cm.moveColumn(index, lc);
50353                     this.grid.fireEvent("columnmove", index, lc);
50354                 }else{
50355                     cm.setLocked(index, true);
50356                 }
50357             break;
50358             case "unlock":
50359                 var lc = cm.getLockedCount();
50360                 if((lc-1) != index){
50361                     cm.setLocked(index, false, true);
50362                     cm.moveColumn(index, lc-1);
50363                     this.grid.fireEvent("columnmove", index, lc-1);
50364                 }else{
50365                     cm.setLocked(index, false);
50366                 }
50367             break;
50368             default:
50369                 index = cm.getIndexById(item.id.substr(4));
50370                 if(index != -1){
50371                     if(item.checked && cm.getColumnCount(true) <= 1){
50372                         this.onDenyColumnHide();
50373                         return false;
50374                     }
50375                     cm.setHidden(index, item.checked);
50376                 }
50377         }
50378         return true;
50379     },
50380
50381     beforeColMenuShow : function(){
50382         var cm = this.cm,  colCount = cm.getColumnCount();
50383         this.colMenu.removeAll();
50384         for(var i = 0; i < colCount; i++){
50385             this.colMenu.add(new Roo.menu.CheckItem({
50386                 id: "col-"+cm.getColumnId(i),
50387                 text: cm.getColumnHeader(i),
50388                 checked: !cm.isHidden(i),
50389                 hideOnClick:false
50390             }));
50391         }
50392     },
50393
50394     handleHdCtx : function(g, index, e){
50395         e.stopEvent();
50396         var hd = this.getHeaderCell(index);
50397         this.hdCtxIndex = index;
50398         var ms = this.hmenu.items, cm = this.cm;
50399         ms.get("asc").setDisabled(!cm.isSortable(index));
50400         ms.get("desc").setDisabled(!cm.isSortable(index));
50401         if(this.grid.enableColLock !== false){
50402             ms.get("lock").setDisabled(cm.isLocked(index));
50403             ms.get("unlock").setDisabled(!cm.isLocked(index));
50404         }
50405         this.hmenu.show(hd, "tl-bl");
50406     },
50407
50408     handleHdOver : function(e){
50409         var hd = this.findHeaderCell(e.getTarget());
50410         if(hd && !this.headersDisabled){
50411             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
50412                this.fly(hd).addClass("x-grid-hd-over");
50413             }
50414         }
50415     },
50416
50417     handleHdOut : function(e){
50418         var hd = this.findHeaderCell(e.getTarget());
50419         if(hd){
50420             this.fly(hd).removeClass("x-grid-hd-over");
50421         }
50422     },
50423
50424     handleSplitDblClick : function(e, t){
50425         var i = this.getCellIndex(t);
50426         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
50427             this.autoSizeColumn(i, true);
50428             this.layout();
50429         }
50430     },
50431
50432     render : function(){
50433
50434         var cm = this.cm;
50435         var colCount = cm.getColumnCount();
50436
50437         if(this.grid.monitorWindowResize === true){
50438             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50439         }
50440         var header = this.renderHeaders();
50441         var body = this.templates.body.apply({rows:""});
50442         var html = this.templates.master.apply({
50443             lockedBody: body,
50444             body: body,
50445             lockedHeader: header[0],
50446             header: header[1]
50447         });
50448
50449         //this.updateColumns();
50450
50451         this.grid.getGridEl().dom.innerHTML = html;
50452
50453         this.initElements();
50454         
50455         // a kludge to fix the random scolling effect in webkit
50456         this.el.on("scroll", function() {
50457             this.el.dom.scrollTop=0; // hopefully not recursive..
50458         },this);
50459
50460         this.scroller.on("scroll", this.handleScroll, this);
50461         this.lockedBody.on("mousewheel", this.handleWheel, this);
50462         this.mainBody.on("mousewheel", this.handleWheel, this);
50463
50464         this.mainHd.on("mouseover", this.handleHdOver, this);
50465         this.mainHd.on("mouseout", this.handleHdOut, this);
50466         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
50467                 {delegate: "."+this.splitClass});
50468
50469         this.lockedHd.on("mouseover", this.handleHdOver, this);
50470         this.lockedHd.on("mouseout", this.handleHdOut, this);
50471         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
50472                 {delegate: "."+this.splitClass});
50473
50474         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
50475             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50476         }
50477
50478         this.updateSplitters();
50479
50480         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
50481             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50482             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
50483         }
50484
50485         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
50486             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
50487             this.hmenu.add(
50488                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
50489                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
50490             );
50491             if(this.grid.enableColLock !== false){
50492                 this.hmenu.add('-',
50493                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
50494                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
50495                 );
50496             }
50497             if(this.grid.enableColumnHide !== false){
50498
50499                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
50500                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
50501                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
50502
50503                 this.hmenu.add('-',
50504                     {id:"columns", text: this.columnsText, menu: this.colMenu}
50505                 );
50506             }
50507             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
50508
50509             this.grid.on("headercontextmenu", this.handleHdCtx, this);
50510         }
50511
50512         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
50513             this.dd = new Roo.grid.GridDragZone(this.grid, {
50514                 ddGroup : this.grid.ddGroup || 'GridDD'
50515             });
50516         }
50517
50518         /*
50519         for(var i = 0; i < colCount; i++){
50520             if(cm.isHidden(i)){
50521                 this.hideColumn(i);
50522             }
50523             if(cm.config[i].align){
50524                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
50525                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
50526             }
50527         }*/
50528         
50529         this.updateHeaderSortState();
50530
50531         this.beforeInitialResize();
50532         this.layout(true);
50533
50534         // two part rendering gives faster view to the user
50535         this.renderPhase2.defer(1, this);
50536     },
50537
50538     renderPhase2 : function(){
50539         // render the rows now
50540         this.refresh();
50541         if(this.grid.autoSizeColumns){
50542             this.autoSizeColumns();
50543         }
50544     },
50545
50546     beforeInitialResize : function(){
50547
50548     },
50549
50550     onColumnSplitterMoved : function(i, w){
50551         this.userResized = true;
50552         var cm = this.grid.colModel;
50553         cm.setColumnWidth(i, w, true);
50554         var cid = cm.getColumnId(i);
50555         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50556         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
50557         this.updateSplitters();
50558         this.layout();
50559         this.grid.fireEvent("columnresize", i, w);
50560     },
50561
50562     syncRowHeights : function(startIndex, endIndex){
50563         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
50564             startIndex = startIndex || 0;
50565             var mrows = this.getBodyTable().rows;
50566             var lrows = this.getLockedTable().rows;
50567             var len = mrows.length-1;
50568             endIndex = Math.min(endIndex || len, len);
50569             for(var i = startIndex; i <= endIndex; i++){
50570                 var m = mrows[i], l = lrows[i];
50571                 var h = Math.max(m.offsetHeight, l.offsetHeight);
50572                 m.style.height = l.style.height = h + "px";
50573             }
50574         }
50575     },
50576
50577     layout : function(initialRender, is2ndPass){
50578         var g = this.grid;
50579         var auto = g.autoHeight;
50580         var scrollOffset = 16;
50581         var c = g.getGridEl(), cm = this.cm,
50582                 expandCol = g.autoExpandColumn,
50583                 gv = this;
50584         //c.beginMeasure();
50585
50586         if(!c.dom.offsetWidth){ // display:none?
50587             if(initialRender){
50588                 this.lockedWrap.show();
50589                 this.mainWrap.show();
50590             }
50591             return;
50592         }
50593
50594         var hasLock = this.cm.isLocked(0);
50595
50596         var tbh = this.headerPanel.getHeight();
50597         var bbh = this.footerPanel.getHeight();
50598
50599         if(auto){
50600             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50601             var newHeight = ch + c.getBorderWidth("tb");
50602             if(g.maxHeight){
50603                 newHeight = Math.min(g.maxHeight, newHeight);
50604             }
50605             c.setHeight(newHeight);
50606         }
50607
50608         if(g.autoWidth){
50609             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50610         }
50611
50612         var s = this.scroller;
50613
50614         var csize = c.getSize(true);
50615
50616         this.el.setSize(csize.width, csize.height);
50617
50618         this.headerPanel.setWidth(csize.width);
50619         this.footerPanel.setWidth(csize.width);
50620
50621         var hdHeight = this.mainHd.getHeight();
50622         var vw = csize.width;
50623         var vh = csize.height - (tbh + bbh);
50624
50625         s.setSize(vw, vh);
50626
50627         var bt = this.getBodyTable();
50628         var ltWidth = hasLock ?
50629                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50630
50631         var scrollHeight = bt.offsetHeight;
50632         var scrollWidth = ltWidth + bt.offsetWidth;
50633         var vscroll = false, hscroll = false;
50634
50635         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50636
50637         var lw = this.lockedWrap, mw = this.mainWrap;
50638         var lb = this.lockedBody, mb = this.mainBody;
50639
50640         setTimeout(function(){
50641             var t = s.dom.offsetTop;
50642             var w = s.dom.clientWidth,
50643                 h = s.dom.clientHeight;
50644
50645             lw.setTop(t);
50646             lw.setSize(ltWidth, h);
50647
50648             mw.setLeftTop(ltWidth, t);
50649             mw.setSize(w-ltWidth, h);
50650
50651             lb.setHeight(h-hdHeight);
50652             mb.setHeight(h-hdHeight);
50653
50654             if(is2ndPass !== true && !gv.userResized && expandCol){
50655                 // high speed resize without full column calculation
50656                 
50657                 var ci = cm.getIndexById(expandCol);
50658                 if (ci < 0) {
50659                     ci = cm.findColumnIndex(expandCol);
50660                 }
50661                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50662                 var expandId = cm.getColumnId(ci);
50663                 var  tw = cm.getTotalWidth(false);
50664                 var currentWidth = cm.getColumnWidth(ci);
50665                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50666                 if(currentWidth != cw){
50667                     cm.setColumnWidth(ci, cw, true);
50668                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50669                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50670                     gv.updateSplitters();
50671                     gv.layout(false, true);
50672                 }
50673             }
50674
50675             if(initialRender){
50676                 lw.show();
50677                 mw.show();
50678             }
50679             //c.endMeasure();
50680         }, 10);
50681     },
50682
50683     onWindowResize : function(){
50684         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50685             return;
50686         }
50687         this.layout();
50688     },
50689
50690     appendFooter : function(parentEl){
50691         return null;
50692     },
50693
50694     sortAscText : "Sort Ascending",
50695     sortDescText : "Sort Descending",
50696     lockText : "Lock Column",
50697     unlockText : "Unlock Column",
50698     columnsText : "Columns"
50699 });
50700
50701
50702 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50703     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50704     this.proxy.el.addClass('x-grid3-col-dd');
50705 };
50706
50707 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50708     handleMouseDown : function(e){
50709
50710     },
50711
50712     callHandleMouseDown : function(e){
50713         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50714     }
50715 });
50716 /*
50717  * Based on:
50718  * Ext JS Library 1.1.1
50719  * Copyright(c) 2006-2007, Ext JS, LLC.
50720  *
50721  * Originally Released Under LGPL - original licence link has changed is not relivant.
50722  *
50723  * Fork - LGPL
50724  * <script type="text/javascript">
50725  */
50726  
50727 // private
50728 // This is a support class used internally by the Grid components
50729 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50730     this.grid = grid;
50731     this.view = grid.getView();
50732     this.proxy = this.view.resizeProxy;
50733     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50734         "gridSplitters" + this.grid.getGridEl().id, {
50735         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50736     });
50737     this.setHandleElId(Roo.id(hd));
50738     this.setOuterHandleElId(Roo.id(hd2));
50739     this.scroll = false;
50740 };
50741 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50742     fly: Roo.Element.fly,
50743
50744     b4StartDrag : function(x, y){
50745         this.view.headersDisabled = true;
50746         this.proxy.setHeight(this.view.mainWrap.getHeight());
50747         var w = this.cm.getColumnWidth(this.cellIndex);
50748         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50749         this.resetConstraints();
50750         this.setXConstraint(minw, 1000);
50751         this.setYConstraint(0, 0);
50752         this.minX = x - minw;
50753         this.maxX = x + 1000;
50754         this.startPos = x;
50755         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50756     },
50757
50758
50759     handleMouseDown : function(e){
50760         ev = Roo.EventObject.setEvent(e);
50761         var t = this.fly(ev.getTarget());
50762         if(t.hasClass("x-grid-split")){
50763             this.cellIndex = this.view.getCellIndex(t.dom);
50764             this.split = t.dom;
50765             this.cm = this.grid.colModel;
50766             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50767                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50768             }
50769         }
50770     },
50771
50772     endDrag : function(e){
50773         this.view.headersDisabled = false;
50774         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50775         var diff = endX - this.startPos;
50776         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50777     },
50778
50779     autoOffset : function(){
50780         this.setDelta(0,0);
50781     }
50782 });/*
50783  * Based on:
50784  * Ext JS Library 1.1.1
50785  * Copyright(c) 2006-2007, Ext JS, LLC.
50786  *
50787  * Originally Released Under LGPL - original licence link has changed is not relivant.
50788  *
50789  * Fork - LGPL
50790  * <script type="text/javascript">
50791  */
50792  
50793 // private
50794 // This is a support class used internally by the Grid components
50795 Roo.grid.GridDragZone = function(grid, config){
50796     this.view = grid.getView();
50797     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50798     if(this.view.lockedBody){
50799         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50800         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50801     }
50802     this.scroll = false;
50803     this.grid = grid;
50804     this.ddel = document.createElement('div');
50805     this.ddel.className = 'x-grid-dd-wrap';
50806 };
50807
50808 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50809     ddGroup : "GridDD",
50810
50811     getDragData : function(e){
50812         var t = Roo.lib.Event.getTarget(e);
50813         var rowIndex = this.view.findRowIndex(t);
50814         if(rowIndex !== false){
50815             var sm = this.grid.selModel;
50816             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50817               //  sm.mouseDown(e, t);
50818             //}
50819             if (e.hasModifier()){
50820                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50821             }
50822             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50823         }
50824         return false;
50825     },
50826
50827     onInitDrag : function(e){
50828         var data = this.dragData;
50829         this.ddel.innerHTML = this.grid.getDragDropText();
50830         this.proxy.update(this.ddel);
50831         // fire start drag?
50832     },
50833
50834     afterRepair : function(){
50835         this.dragging = false;
50836     },
50837
50838     getRepairXY : function(e, data){
50839         return false;
50840     },
50841
50842     onEndDrag : function(data, e){
50843         // fire end drag?
50844     },
50845
50846     onValidDrop : function(dd, e, id){
50847         // fire drag drop?
50848         this.hideProxy();
50849     },
50850
50851     beforeInvalidDrop : function(e, id){
50852
50853     }
50854 });/*
50855  * Based on:
50856  * Ext JS Library 1.1.1
50857  * Copyright(c) 2006-2007, Ext JS, LLC.
50858  *
50859  * Originally Released Under LGPL - original licence link has changed is not relivant.
50860  *
50861  * Fork - LGPL
50862  * <script type="text/javascript">
50863  */
50864  
50865
50866 /**
50867  * @class Roo.grid.ColumnModel
50868  * @extends Roo.util.Observable
50869  * This is the default implementation of a ColumnModel used by the Grid. It defines
50870  * the columns in the grid.
50871  * <br>Usage:<br>
50872  <pre><code>
50873  var colModel = new Roo.grid.ColumnModel([
50874         {header: "Ticker", width: 60, sortable: true, locked: true},
50875         {header: "Company Name", width: 150, sortable: true},
50876         {header: "Market Cap.", width: 100, sortable: true},
50877         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50878         {header: "Employees", width: 100, sortable: true, resizable: false}
50879  ]);
50880  </code></pre>
50881  * <p>
50882  
50883  * The config options listed for this class are options which may appear in each
50884  * individual column definition.
50885  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50886  * @constructor
50887  * @param {Object} config An Array of column config objects. See this class's
50888  * config objects for details.
50889 */
50890 Roo.grid.ColumnModel = function(config){
50891         /**
50892      * The config passed into the constructor
50893      */
50894     this.config = config;
50895     this.lookup = {};
50896
50897     // if no id, create one
50898     // if the column does not have a dataIndex mapping,
50899     // map it to the order it is in the config
50900     for(var i = 0, len = config.length; i < len; i++){
50901         var c = config[i];
50902         if(typeof c.dataIndex == "undefined"){
50903             c.dataIndex = i;
50904         }
50905         if(typeof c.renderer == "string"){
50906             c.renderer = Roo.util.Format[c.renderer];
50907         }
50908         if(typeof c.id == "undefined"){
50909             c.id = Roo.id();
50910         }
50911         if(c.editor && c.editor.xtype){
50912             c.editor  = Roo.factory(c.editor, Roo.grid);
50913         }
50914         if(c.editor && c.editor.isFormField){
50915             c.editor = new Roo.grid.GridEditor(c.editor);
50916         }
50917         this.lookup[c.id] = c;
50918     }
50919
50920     /**
50921      * The width of columns which have no width specified (defaults to 100)
50922      * @type Number
50923      */
50924     this.defaultWidth = 100;
50925
50926     /**
50927      * Default sortable of columns which have no sortable specified (defaults to false)
50928      * @type Boolean
50929      */
50930     this.defaultSortable = false;
50931
50932     this.addEvents({
50933         /**
50934              * @event widthchange
50935              * Fires when the width of a column changes.
50936              * @param {ColumnModel} this
50937              * @param {Number} columnIndex The column index
50938              * @param {Number} newWidth The new width
50939              */
50940             "widthchange": true,
50941         /**
50942              * @event headerchange
50943              * Fires when the text of a header changes.
50944              * @param {ColumnModel} this
50945              * @param {Number} columnIndex The column index
50946              * @param {Number} newText The new header text
50947              */
50948             "headerchange": true,
50949         /**
50950              * @event hiddenchange
50951              * Fires when a column is hidden or "unhidden".
50952              * @param {ColumnModel} this
50953              * @param {Number} columnIndex The column index
50954              * @param {Boolean} hidden true if hidden, false otherwise
50955              */
50956             "hiddenchange": true,
50957             /**
50958          * @event columnmoved
50959          * Fires when a column is moved.
50960          * @param {ColumnModel} this
50961          * @param {Number} oldIndex
50962          * @param {Number} newIndex
50963          */
50964         "columnmoved" : true,
50965         /**
50966          * @event columlockchange
50967          * Fires when a column's locked state is changed
50968          * @param {ColumnModel} this
50969          * @param {Number} colIndex
50970          * @param {Boolean} locked true if locked
50971          */
50972         "columnlockchange" : true
50973     });
50974     Roo.grid.ColumnModel.superclass.constructor.call(this);
50975 };
50976 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50977     /**
50978      * @cfg {String} header The header text to display in the Grid view.
50979      */
50980     /**
50981      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50982      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50983      * specified, the column's index is used as an index into the Record's data Array.
50984      */
50985     /**
50986      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50987      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50988      */
50989     /**
50990      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50991      * Defaults to the value of the {@link #defaultSortable} property.
50992      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50993      */
50994     /**
50995      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50996      */
50997     /**
50998      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50999      */
51000     /**
51001      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
51002      */
51003     /**
51004      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
51005      */
51006     /**
51007      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
51008      * given the cell's data value. See {@link #setRenderer}. If not specified, the
51009      * default renderer uses the raw data value.
51010      */
51011        /**
51012      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
51013      */
51014     /**
51015      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
51016      */
51017
51018     /**
51019      * Returns the id of the column at the specified index.
51020      * @param {Number} index The column index
51021      * @return {String} the id
51022      */
51023     getColumnId : function(index){
51024         return this.config[index].id;
51025     },
51026
51027     /**
51028      * Returns the column for a specified id.
51029      * @param {String} id The column id
51030      * @return {Object} the column
51031      */
51032     getColumnById : function(id){
51033         return this.lookup[id];
51034     },
51035
51036     
51037     /**
51038      * Returns the column for a specified dataIndex.
51039      * @param {String} dataIndex The column dataIndex
51040      * @return {Object|Boolean} the column or false if not found
51041      */
51042     getColumnByDataIndex: function(dataIndex){
51043         var index = this.findColumnIndex(dataIndex);
51044         return index > -1 ? this.config[index] : false;
51045     },
51046     
51047     /**
51048      * Returns the index for a specified column id.
51049      * @param {String} id The column id
51050      * @return {Number} the index, or -1 if not found
51051      */
51052     getIndexById : function(id){
51053         for(var i = 0, len = this.config.length; i < len; i++){
51054             if(this.config[i].id == id){
51055                 return i;
51056             }
51057         }
51058         return -1;
51059     },
51060     
51061     /**
51062      * Returns the index for a specified column dataIndex.
51063      * @param {String} dataIndex The column dataIndex
51064      * @return {Number} the index, or -1 if not found
51065      */
51066     
51067     findColumnIndex : function(dataIndex){
51068         for(var i = 0, len = this.config.length; i < len; i++){
51069             if(this.config[i].dataIndex == dataIndex){
51070                 return i;
51071             }
51072         }
51073         return -1;
51074     },
51075     
51076     
51077     moveColumn : function(oldIndex, newIndex){
51078         var c = this.config[oldIndex];
51079         this.config.splice(oldIndex, 1);
51080         this.config.splice(newIndex, 0, c);
51081         this.dataMap = null;
51082         this.fireEvent("columnmoved", this, oldIndex, newIndex);
51083     },
51084
51085     isLocked : function(colIndex){
51086         return this.config[colIndex].locked === true;
51087     },
51088
51089     setLocked : function(colIndex, value, suppressEvent){
51090         if(this.isLocked(colIndex) == value){
51091             return;
51092         }
51093         this.config[colIndex].locked = value;
51094         if(!suppressEvent){
51095             this.fireEvent("columnlockchange", this, colIndex, value);
51096         }
51097     },
51098
51099     getTotalLockedWidth : function(){
51100         var totalWidth = 0;
51101         for(var i = 0; i < this.config.length; i++){
51102             if(this.isLocked(i) && !this.isHidden(i)){
51103                 this.totalWidth += this.getColumnWidth(i);
51104             }
51105         }
51106         return totalWidth;
51107     },
51108
51109     getLockedCount : function(){
51110         for(var i = 0, len = this.config.length; i < len; i++){
51111             if(!this.isLocked(i)){
51112                 return i;
51113             }
51114         }
51115     },
51116
51117     /**
51118      * Returns the number of columns.
51119      * @return {Number}
51120      */
51121     getColumnCount : function(visibleOnly){
51122         if(visibleOnly === true){
51123             var c = 0;
51124             for(var i = 0, len = this.config.length; i < len; i++){
51125                 if(!this.isHidden(i)){
51126                     c++;
51127                 }
51128             }
51129             return c;
51130         }
51131         return this.config.length;
51132     },
51133
51134     /**
51135      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
51136      * @param {Function} fn
51137      * @param {Object} scope (optional)
51138      * @return {Array} result
51139      */
51140     getColumnsBy : function(fn, scope){
51141         var r = [];
51142         for(var i = 0, len = this.config.length; i < len; i++){
51143             var c = this.config[i];
51144             if(fn.call(scope||this, c, i) === true){
51145                 r[r.length] = c;
51146             }
51147         }
51148         return r;
51149     },
51150
51151     /**
51152      * Returns true if the specified column is sortable.
51153      * @param {Number} col The column index
51154      * @return {Boolean}
51155      */
51156     isSortable : function(col){
51157         if(typeof this.config[col].sortable == "undefined"){
51158             return this.defaultSortable;
51159         }
51160         return this.config[col].sortable;
51161     },
51162
51163     /**
51164      * Returns the rendering (formatting) function defined for the column.
51165      * @param {Number} col The column index.
51166      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
51167      */
51168     getRenderer : function(col){
51169         if(!this.config[col].renderer){
51170             return Roo.grid.ColumnModel.defaultRenderer;
51171         }
51172         return this.config[col].renderer;
51173     },
51174
51175     /**
51176      * Sets the rendering (formatting) function for a column.
51177      * @param {Number} col The column index
51178      * @param {Function} fn The function to use to process the cell's raw data
51179      * to return HTML markup for the grid view. The render function is called with
51180      * the following parameters:<ul>
51181      * <li>Data value.</li>
51182      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
51183      * <li>css A CSS style string to apply to the table cell.</li>
51184      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
51185      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
51186      * <li>Row index</li>
51187      * <li>Column index</li>
51188      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
51189      */
51190     setRenderer : function(col, fn){
51191         this.config[col].renderer = fn;
51192     },
51193
51194     /**
51195      * Returns the width for the specified column.
51196      * @param {Number} col The column index
51197      * @return {Number}
51198      */
51199     getColumnWidth : function(col){
51200         return this.config[col].width * 1 || this.defaultWidth;
51201     },
51202
51203     /**
51204      * Sets the width for a column.
51205      * @param {Number} col The column index
51206      * @param {Number} width The new width
51207      */
51208     setColumnWidth : function(col, width, suppressEvent){
51209         this.config[col].width = width;
51210         this.totalWidth = null;
51211         if(!suppressEvent){
51212              this.fireEvent("widthchange", this, col, width);
51213         }
51214     },
51215
51216     /**
51217      * Returns the total width of all columns.
51218      * @param {Boolean} includeHidden True to include hidden column widths
51219      * @return {Number}
51220      */
51221     getTotalWidth : function(includeHidden){
51222         if(!this.totalWidth){
51223             this.totalWidth = 0;
51224             for(var i = 0, len = this.config.length; i < len; i++){
51225                 if(includeHidden || !this.isHidden(i)){
51226                     this.totalWidth += this.getColumnWidth(i);
51227                 }
51228             }
51229         }
51230         return this.totalWidth;
51231     },
51232
51233     /**
51234      * Returns the header for the specified column.
51235      * @param {Number} col The column index
51236      * @return {String}
51237      */
51238     getColumnHeader : function(col){
51239         return this.config[col].header;
51240     },
51241
51242     /**
51243      * Sets the header for a column.
51244      * @param {Number} col The column index
51245      * @param {String} header The new header
51246      */
51247     setColumnHeader : function(col, header){
51248         this.config[col].header = header;
51249         this.fireEvent("headerchange", this, col, header);
51250     },
51251
51252     /**
51253      * Returns the tooltip for the specified column.
51254      * @param {Number} col The column index
51255      * @return {String}
51256      */
51257     getColumnTooltip : function(col){
51258             return this.config[col].tooltip;
51259     },
51260     /**
51261      * Sets the tooltip for a column.
51262      * @param {Number} col The column index
51263      * @param {String} tooltip The new tooltip
51264      */
51265     setColumnTooltip : function(col, tooltip){
51266             this.config[col].tooltip = tooltip;
51267     },
51268
51269     /**
51270      * Returns the dataIndex for the specified column.
51271      * @param {Number} col The column index
51272      * @return {Number}
51273      */
51274     getDataIndex : function(col){
51275         return this.config[col].dataIndex;
51276     },
51277
51278     /**
51279      * Sets the dataIndex for a column.
51280      * @param {Number} col The column index
51281      * @param {Number} dataIndex The new dataIndex
51282      */
51283     setDataIndex : function(col, dataIndex){
51284         this.config[col].dataIndex = dataIndex;
51285     },
51286
51287     
51288     
51289     /**
51290      * Returns true if the cell is editable.
51291      * @param {Number} colIndex The column index
51292      * @param {Number} rowIndex The row index
51293      * @return {Boolean}
51294      */
51295     isCellEditable : function(colIndex, rowIndex){
51296         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
51297     },
51298
51299     /**
51300      * Returns the editor defined for the cell/column.
51301      * return false or null to disable editing.
51302      * @param {Number} colIndex The column index
51303      * @param {Number} rowIndex The row index
51304      * @return {Object}
51305      */
51306     getCellEditor : function(colIndex, rowIndex){
51307         return this.config[colIndex].editor;
51308     },
51309
51310     /**
51311      * Sets if a column is editable.
51312      * @param {Number} col The column index
51313      * @param {Boolean} editable True if the column is editable
51314      */
51315     setEditable : function(col, editable){
51316         this.config[col].editable = editable;
51317     },
51318
51319
51320     /**
51321      * Returns true if the column is hidden.
51322      * @param {Number} colIndex The column index
51323      * @return {Boolean}
51324      */
51325     isHidden : function(colIndex){
51326         return this.config[colIndex].hidden;
51327     },
51328
51329
51330     /**
51331      * Returns true if the column width cannot be changed
51332      */
51333     isFixed : function(colIndex){
51334         return this.config[colIndex].fixed;
51335     },
51336
51337     /**
51338      * Returns true if the column can be resized
51339      * @return {Boolean}
51340      */
51341     isResizable : function(colIndex){
51342         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
51343     },
51344     /**
51345      * Sets if a column is hidden.
51346      * @param {Number} colIndex The column index
51347      * @param {Boolean} hidden True if the column is hidden
51348      */
51349     setHidden : function(colIndex, hidden){
51350         this.config[colIndex].hidden = hidden;
51351         this.totalWidth = null;
51352         this.fireEvent("hiddenchange", this, colIndex, hidden);
51353     },
51354
51355     /**
51356      * Sets the editor for a column.
51357      * @param {Number} col The column index
51358      * @param {Object} editor The editor object
51359      */
51360     setEditor : function(col, editor){
51361         this.config[col].editor = editor;
51362     }
51363 });
51364
51365 Roo.grid.ColumnModel.defaultRenderer = function(value){
51366         if(typeof value == "string" && value.length < 1){
51367             return "&#160;";
51368         }
51369         return value;
51370 };
51371
51372 // Alias for backwards compatibility
51373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
51374 /*
51375  * Based on:
51376  * Ext JS Library 1.1.1
51377  * Copyright(c) 2006-2007, Ext JS, LLC.
51378  *
51379  * Originally Released Under LGPL - original licence link has changed is not relivant.
51380  *
51381  * Fork - LGPL
51382  * <script type="text/javascript">
51383  */
51384
51385 /**
51386  * @class Roo.grid.AbstractSelectionModel
51387  * @extends Roo.util.Observable
51388  * Abstract base class for grid SelectionModels.  It provides the interface that should be
51389  * implemented by descendant classes.  This class should not be directly instantiated.
51390  * @constructor
51391  */
51392 Roo.grid.AbstractSelectionModel = function(){
51393     this.locked = false;
51394     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
51395 };
51396
51397 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
51398     /** @ignore Called by the grid automatically. Do not call directly. */
51399     init : function(grid){
51400         this.grid = grid;
51401         this.initEvents();
51402     },
51403
51404     /**
51405      * Locks the selections.
51406      */
51407     lock : function(){
51408         this.locked = true;
51409     },
51410
51411     /**
51412      * Unlocks the selections.
51413      */
51414     unlock : function(){
51415         this.locked = false;
51416     },
51417
51418     /**
51419      * Returns true if the selections are locked.
51420      * @return {Boolean}
51421      */
51422     isLocked : function(){
51423         return this.locked;
51424     }
51425 });/*
51426  * Based on:
51427  * Ext JS Library 1.1.1
51428  * Copyright(c) 2006-2007, Ext JS, LLC.
51429  *
51430  * Originally Released Under LGPL - original licence link has changed is not relivant.
51431  *
51432  * Fork - LGPL
51433  * <script type="text/javascript">
51434  */
51435 /**
51436  * @extends Roo.grid.AbstractSelectionModel
51437  * @class Roo.grid.RowSelectionModel
51438  * The default SelectionModel used by {@link Roo.grid.Grid}.
51439  * It supports multiple selections and keyboard selection/navigation. 
51440  * @constructor
51441  * @param {Object} config
51442  */
51443 Roo.grid.RowSelectionModel = function(config){
51444     Roo.apply(this, config);
51445     this.selections = new Roo.util.MixedCollection(false, function(o){
51446         return o.id;
51447     });
51448
51449     this.last = false;
51450     this.lastActive = false;
51451
51452     this.addEvents({
51453         /**
51454              * @event selectionchange
51455              * Fires when the selection changes
51456              * @param {SelectionModel} this
51457              */
51458             "selectionchange" : true,
51459         /**
51460              * @event afterselectionchange
51461              * Fires after the selection changes (eg. by key press or clicking)
51462              * @param {SelectionModel} this
51463              */
51464             "afterselectionchange" : true,
51465         /**
51466              * @event beforerowselect
51467              * Fires when a row is selected being selected, return false to cancel.
51468              * @param {SelectionModel} this
51469              * @param {Number} rowIndex The selected index
51470              * @param {Boolean} keepExisting False if other selections will be cleared
51471              */
51472             "beforerowselect" : true,
51473         /**
51474              * @event rowselect
51475              * Fires when a row is selected.
51476              * @param {SelectionModel} this
51477              * @param {Number} rowIndex The selected index
51478              * @param {Roo.data.Record} r The record
51479              */
51480             "rowselect" : true,
51481         /**
51482              * @event rowdeselect
51483              * Fires when a row is deselected.
51484              * @param {SelectionModel} this
51485              * @param {Number} rowIndex The selected index
51486              */
51487         "rowdeselect" : true
51488     });
51489     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
51490     this.locked = false;
51491 };
51492
51493 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
51494     /**
51495      * @cfg {Boolean} singleSelect
51496      * True to allow selection of only one row at a time (defaults to false)
51497      */
51498     singleSelect : false,
51499
51500     // private
51501     initEvents : function(){
51502
51503         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
51504             this.grid.on("mousedown", this.handleMouseDown, this);
51505         }else{ // allow click to work like normal
51506             this.grid.on("rowclick", this.handleDragableRowClick, this);
51507         }
51508
51509         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
51510             "up" : function(e){
51511                 if(!e.shiftKey){
51512                     this.selectPrevious(e.shiftKey);
51513                 }else if(this.last !== false && this.lastActive !== false){
51514                     var last = this.last;
51515                     this.selectRange(this.last,  this.lastActive-1);
51516                     this.grid.getView().focusRow(this.lastActive);
51517                     if(last !== false){
51518                         this.last = last;
51519                     }
51520                 }else{
51521                     this.selectFirstRow();
51522                 }
51523                 this.fireEvent("afterselectionchange", this);
51524             },
51525             "down" : function(e){
51526                 if(!e.shiftKey){
51527                     this.selectNext(e.shiftKey);
51528                 }else if(this.last !== false && this.lastActive !== false){
51529                     var last = this.last;
51530                     this.selectRange(this.last,  this.lastActive+1);
51531                     this.grid.getView().focusRow(this.lastActive);
51532                     if(last !== false){
51533                         this.last = last;
51534                     }
51535                 }else{
51536                     this.selectFirstRow();
51537                 }
51538                 this.fireEvent("afterselectionchange", this);
51539             },
51540             scope: this
51541         });
51542
51543         var view = this.grid.view;
51544         view.on("refresh", this.onRefresh, this);
51545         view.on("rowupdated", this.onRowUpdated, this);
51546         view.on("rowremoved", this.onRemove, this);
51547     },
51548
51549     // private
51550     onRefresh : function(){
51551         var ds = this.grid.dataSource, i, v = this.grid.view;
51552         var s = this.selections;
51553         s.each(function(r){
51554             if((i = ds.indexOfId(r.id)) != -1){
51555                 v.onRowSelect(i);
51556             }else{
51557                 s.remove(r);
51558             }
51559         });
51560     },
51561
51562     // private
51563     onRemove : function(v, index, r){
51564         this.selections.remove(r);
51565     },
51566
51567     // private
51568     onRowUpdated : function(v, index, r){
51569         if(this.isSelected(r)){
51570             v.onRowSelect(index);
51571         }
51572     },
51573
51574     /**
51575      * Select records.
51576      * @param {Array} records The records to select
51577      * @param {Boolean} keepExisting (optional) True to keep existing selections
51578      */
51579     selectRecords : function(records, keepExisting){
51580         if(!keepExisting){
51581             this.clearSelections();
51582         }
51583         var ds = this.grid.dataSource;
51584         for(var i = 0, len = records.length; i < len; i++){
51585             this.selectRow(ds.indexOf(records[i]), true);
51586         }
51587     },
51588
51589     /**
51590      * Gets the number of selected rows.
51591      * @return {Number}
51592      */
51593     getCount : function(){
51594         return this.selections.length;
51595     },
51596
51597     /**
51598      * Selects the first row in the grid.
51599      */
51600     selectFirstRow : function(){
51601         this.selectRow(0);
51602     },
51603
51604     /**
51605      * Select the last row.
51606      * @param {Boolean} keepExisting (optional) True to keep existing selections
51607      */
51608     selectLastRow : function(keepExisting){
51609         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51610     },
51611
51612     /**
51613      * Selects the row immediately following the last selected row.
51614      * @param {Boolean} keepExisting (optional) True to keep existing selections
51615      */
51616     selectNext : function(keepExisting){
51617         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51618             this.selectRow(this.last+1, keepExisting);
51619             this.grid.getView().focusRow(this.last);
51620         }
51621     },
51622
51623     /**
51624      * Selects the row that precedes the last selected row.
51625      * @param {Boolean} keepExisting (optional) True to keep existing selections
51626      */
51627     selectPrevious : function(keepExisting){
51628         if(this.last){
51629             this.selectRow(this.last-1, keepExisting);
51630             this.grid.getView().focusRow(this.last);
51631         }
51632     },
51633
51634     /**
51635      * Returns the selected records
51636      * @return {Array} Array of selected records
51637      */
51638     getSelections : function(){
51639         return [].concat(this.selections.items);
51640     },
51641
51642     /**
51643      * Returns the first selected record.
51644      * @return {Record}
51645      */
51646     getSelected : function(){
51647         return this.selections.itemAt(0);
51648     },
51649
51650
51651     /**
51652      * Clears all selections.
51653      */
51654     clearSelections : function(fast){
51655         if(this.locked) return;
51656         if(fast !== true){
51657             var ds = this.grid.dataSource;
51658             var s = this.selections;
51659             s.each(function(r){
51660                 this.deselectRow(ds.indexOfId(r.id));
51661             }, this);
51662             s.clear();
51663         }else{
51664             this.selections.clear();
51665         }
51666         this.last = false;
51667     },
51668
51669
51670     /**
51671      * Selects all rows.
51672      */
51673     selectAll : function(){
51674         if(this.locked) return;
51675         this.selections.clear();
51676         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51677             this.selectRow(i, true);
51678         }
51679     },
51680
51681     /**
51682      * Returns True if there is a selection.
51683      * @return {Boolean}
51684      */
51685     hasSelection : function(){
51686         return this.selections.length > 0;
51687     },
51688
51689     /**
51690      * Returns True if the specified row is selected.
51691      * @param {Number/Record} record The record or index of the record to check
51692      * @return {Boolean}
51693      */
51694     isSelected : function(index){
51695         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51696         return (r && this.selections.key(r.id) ? true : false);
51697     },
51698
51699     /**
51700      * Returns True if the specified record id is selected.
51701      * @param {String} id The id of record to check
51702      * @return {Boolean}
51703      */
51704     isIdSelected : function(id){
51705         return (this.selections.key(id) ? true : false);
51706     },
51707
51708     // private
51709     handleMouseDown : function(e, t){
51710         var view = this.grid.getView(), rowIndex;
51711         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51712             return;
51713         };
51714         if(e.shiftKey && this.last !== false){
51715             var last = this.last;
51716             this.selectRange(last, rowIndex, e.ctrlKey);
51717             this.last = last; // reset the last
51718             view.focusRow(rowIndex);
51719         }else{
51720             var isSelected = this.isSelected(rowIndex);
51721             if(e.button !== 0 && isSelected){
51722                 view.focusRow(rowIndex);
51723             }else if(e.ctrlKey && isSelected){
51724                 this.deselectRow(rowIndex);
51725             }else if(!isSelected){
51726                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51727                 view.focusRow(rowIndex);
51728             }
51729         }
51730         this.fireEvent("afterselectionchange", this);
51731     },
51732     // private
51733     handleDragableRowClick :  function(grid, rowIndex, e) 
51734     {
51735         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51736             this.selectRow(rowIndex, false);
51737             grid.view.focusRow(rowIndex);
51738              this.fireEvent("afterselectionchange", this);
51739         }
51740     },
51741     
51742     /**
51743      * Selects multiple rows.
51744      * @param {Array} rows Array of the indexes of the row to select
51745      * @param {Boolean} keepExisting (optional) True to keep existing selections
51746      */
51747     selectRows : function(rows, keepExisting){
51748         if(!keepExisting){
51749             this.clearSelections();
51750         }
51751         for(var i = 0, len = rows.length; i < len; i++){
51752             this.selectRow(rows[i], true);
51753         }
51754     },
51755
51756     /**
51757      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51758      * @param {Number} startRow The index of the first row in the range
51759      * @param {Number} endRow The index of the last row in the range
51760      * @param {Boolean} keepExisting (optional) True to retain existing selections
51761      */
51762     selectRange : function(startRow, endRow, keepExisting){
51763         if(this.locked) return;
51764         if(!keepExisting){
51765             this.clearSelections();
51766         }
51767         if(startRow <= endRow){
51768             for(var i = startRow; i <= endRow; i++){
51769                 this.selectRow(i, true);
51770             }
51771         }else{
51772             for(var i = startRow; i >= endRow; i--){
51773                 this.selectRow(i, true);
51774             }
51775         }
51776     },
51777
51778     /**
51779      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51780      * @param {Number} startRow The index of the first row in the range
51781      * @param {Number} endRow The index of the last row in the range
51782      */
51783     deselectRange : function(startRow, endRow, preventViewNotify){
51784         if(this.locked) return;
51785         for(var i = startRow; i <= endRow; i++){
51786             this.deselectRow(i, preventViewNotify);
51787         }
51788     },
51789
51790     /**
51791      * Selects a row.
51792      * @param {Number} row The index of the row to select
51793      * @param {Boolean} keepExisting (optional) True to keep existing selections
51794      */
51795     selectRow : function(index, keepExisting, preventViewNotify){
51796         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51797         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51798             if(!keepExisting || this.singleSelect){
51799                 this.clearSelections();
51800             }
51801             var r = this.grid.dataSource.getAt(index);
51802             this.selections.add(r);
51803             this.last = this.lastActive = index;
51804             if(!preventViewNotify){
51805                 this.grid.getView().onRowSelect(index);
51806             }
51807             this.fireEvent("rowselect", this, index, r);
51808             this.fireEvent("selectionchange", this);
51809         }
51810     },
51811
51812     /**
51813      * Deselects a row.
51814      * @param {Number} row The index of the row to deselect
51815      */
51816     deselectRow : function(index, preventViewNotify){
51817         if(this.locked) return;
51818         if(this.last == index){
51819             this.last = false;
51820         }
51821         if(this.lastActive == index){
51822             this.lastActive = false;
51823         }
51824         var r = this.grid.dataSource.getAt(index);
51825         this.selections.remove(r);
51826         if(!preventViewNotify){
51827             this.grid.getView().onRowDeselect(index);
51828         }
51829         this.fireEvent("rowdeselect", this, index);
51830         this.fireEvent("selectionchange", this);
51831     },
51832
51833     // private
51834     restoreLast : function(){
51835         if(this._last){
51836             this.last = this._last;
51837         }
51838     },
51839
51840     // private
51841     acceptsNav : function(row, col, cm){
51842         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51843     },
51844
51845     // private
51846     onEditorKey : function(field, e){
51847         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51848         if(k == e.TAB){
51849             e.stopEvent();
51850             ed.completeEdit();
51851             if(e.shiftKey){
51852                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51853             }else{
51854                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51855             }
51856         }else if(k == e.ENTER && !e.ctrlKey){
51857             e.stopEvent();
51858             ed.completeEdit();
51859             if(e.shiftKey){
51860                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51861             }else{
51862                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51863             }
51864         }else if(k == e.ESC){
51865             ed.cancelEdit();
51866         }
51867         if(newCell){
51868             g.startEditing(newCell[0], newCell[1]);
51869         }
51870     }
51871 });/*
51872  * Based on:
51873  * Ext JS Library 1.1.1
51874  * Copyright(c) 2006-2007, Ext JS, LLC.
51875  *
51876  * Originally Released Under LGPL - original licence link has changed is not relivant.
51877  *
51878  * Fork - LGPL
51879  * <script type="text/javascript">
51880  */
51881 /**
51882  * @class Roo.grid.CellSelectionModel
51883  * @extends Roo.grid.AbstractSelectionModel
51884  * This class provides the basic implementation for cell selection in a grid.
51885  * @constructor
51886  * @param {Object} config The object containing the configuration of this model.
51887  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51888  */
51889 Roo.grid.CellSelectionModel = function(config){
51890     Roo.apply(this, config);
51891
51892     this.selection = null;
51893
51894     this.addEvents({
51895         /**
51896              * @event beforerowselect
51897              * Fires before a cell is selected.
51898              * @param {SelectionModel} this
51899              * @param {Number} rowIndex The selected row index
51900              * @param {Number} colIndex The selected cell index
51901              */
51902             "beforecellselect" : true,
51903         /**
51904              * @event cellselect
51905              * Fires when a cell is selected.
51906              * @param {SelectionModel} this
51907              * @param {Number} rowIndex The selected row index
51908              * @param {Number} colIndex The selected cell index
51909              */
51910             "cellselect" : true,
51911         /**
51912              * @event selectionchange
51913              * Fires when the active selection changes.
51914              * @param {SelectionModel} this
51915              * @param {Object} selection null for no selection or an object (o) with two properties
51916                 <ul>
51917                 <li>o.record: the record object for the row the selection is in</li>
51918                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51919                 </ul>
51920              */
51921             "selectionchange" : true,
51922         /**
51923              * @event tabend
51924              * Fires when the tab (or enter) was pressed on the last editable cell
51925              * You can use this to trigger add new row.
51926              * @param {SelectionModel} this
51927              */
51928             "tabend" : true,
51929          /**
51930              * @event beforeeditnext
51931              * Fires before the next editable sell is made active
51932              * You can use this to skip to another cell or fire the tabend
51933              *    if you set cell to false
51934              * @param {Object} eventdata object : { cell : [ row, col ] } 
51935              */
51936             "beforeeditnext" : true
51937     });
51938     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51939 };
51940
51941 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51942     
51943     enter_is_tab: false,
51944
51945     /** @ignore */
51946     initEvents : function(){
51947         this.grid.on("mousedown", this.handleMouseDown, this);
51948         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51949         var view = this.grid.view;
51950         view.on("refresh", this.onViewChange, this);
51951         view.on("rowupdated", this.onRowUpdated, this);
51952         view.on("beforerowremoved", this.clearSelections, this);
51953         view.on("beforerowsinserted", this.clearSelections, this);
51954         if(this.grid.isEditor){
51955             this.grid.on("beforeedit", this.beforeEdit,  this);
51956         }
51957     },
51958
51959         //private
51960     beforeEdit : function(e){
51961         this.select(e.row, e.column, false, true, e.record);
51962     },
51963
51964         //private
51965     onRowUpdated : function(v, index, r){
51966         if(this.selection && this.selection.record == r){
51967             v.onCellSelect(index, this.selection.cell[1]);
51968         }
51969     },
51970
51971         //private
51972     onViewChange : function(){
51973         this.clearSelections(true);
51974     },
51975
51976         /**
51977          * Returns the currently selected cell,.
51978          * @return {Array} The selected cell (row, column) or null if none selected.
51979          */
51980     getSelectedCell : function(){
51981         return this.selection ? this.selection.cell : null;
51982     },
51983
51984     /**
51985      * Clears all selections.
51986      * @param {Boolean} true to prevent the gridview from being notified about the change.
51987      */
51988     clearSelections : function(preventNotify){
51989         var s = this.selection;
51990         if(s){
51991             if(preventNotify !== true){
51992                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51993             }
51994             this.selection = null;
51995             this.fireEvent("selectionchange", this, null);
51996         }
51997     },
51998
51999     /**
52000      * Returns true if there is a selection.
52001      * @return {Boolean}
52002      */
52003     hasSelection : function(){
52004         return this.selection ? true : false;
52005     },
52006
52007     /** @ignore */
52008     handleMouseDown : function(e, t){
52009         var v = this.grid.getView();
52010         if(this.isLocked()){
52011             return;
52012         };
52013         var row = v.findRowIndex(t);
52014         var cell = v.findCellIndex(t);
52015         if(row !== false && cell !== false){
52016             this.select(row, cell);
52017         }
52018     },
52019
52020     /**
52021      * Selects a cell.
52022      * @param {Number} rowIndex
52023      * @param {Number} collIndex
52024      */
52025     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
52026         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
52027             this.clearSelections();
52028             r = r || this.grid.dataSource.getAt(rowIndex);
52029             this.selection = {
52030                 record : r,
52031                 cell : [rowIndex, colIndex]
52032             };
52033             if(!preventViewNotify){
52034                 var v = this.grid.getView();
52035                 v.onCellSelect(rowIndex, colIndex);
52036                 if(preventFocus !== true){
52037                     v.focusCell(rowIndex, colIndex);
52038                 }
52039             }
52040             this.fireEvent("cellselect", this, rowIndex, colIndex);
52041             this.fireEvent("selectionchange", this, this.selection);
52042         }
52043     },
52044
52045         //private
52046     isSelectable : function(rowIndex, colIndex, cm){
52047         return !cm.isHidden(colIndex);
52048     },
52049
52050     /** @ignore */
52051     handleKeyDown : function(e){
52052         //Roo.log('Cell Sel Model handleKeyDown');
52053         if(!e.isNavKeyPress()){
52054             return;
52055         }
52056         var g = this.grid, s = this.selection;
52057         if(!s){
52058             e.stopEvent();
52059             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
52060             if(cell){
52061                 this.select(cell[0], cell[1]);
52062             }
52063             return;
52064         }
52065         var sm = this;
52066         var walk = function(row, col, step){
52067             return g.walkCells(row, col, step, sm.isSelectable,  sm);
52068         };
52069         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
52070         var newCell;
52071
52072       
52073
52074         switch(k){
52075             case e.TAB:
52076                 // handled by onEditorKey
52077                 if (g.isEditor && g.editing) {
52078                     return;
52079                 }
52080                 if(e.shiftKey) {
52081                     newCell = walk(r, c-1, -1);
52082                 } else {
52083                     newCell = walk(r, c+1, 1);
52084                 }
52085                 break;
52086             
52087             case e.DOWN:
52088                newCell = walk(r+1, c, 1);
52089                 break;
52090             
52091             case e.UP:
52092                 newCell = walk(r-1, c, -1);
52093                 break;
52094             
52095             case e.RIGHT:
52096                 newCell = walk(r, c+1, 1);
52097                 break;
52098             
52099             case e.LEFT:
52100                 newCell = walk(r, c-1, -1);
52101                 break;
52102             
52103             case e.ENTER:
52104                 
52105                 if(g.isEditor && !g.editing){
52106                    g.startEditing(r, c);
52107                    e.stopEvent();
52108                    return;
52109                 }
52110                 
52111                 
52112              break;
52113         };
52114         if(newCell){
52115             this.select(newCell[0], newCell[1]);
52116             e.stopEvent();
52117             
52118         }
52119     },
52120
52121     acceptsNav : function(row, col, cm){
52122         return !cm.isHidden(col) && cm.isCellEditable(col, row);
52123     },
52124     /**
52125      * Selects a cell.
52126      * @param {Number} field (not used) - as it's normally used as a listener
52127      * @param {Number} e - event - fake it by using
52128      *
52129      * var e = Roo.EventObjectImpl.prototype;
52130      * e.keyCode = e.TAB
52131      *
52132      * 
52133      */
52134     onEditorKey : function(field, e){
52135         
52136         var k = e.getKey(),
52137             newCell,
52138             g = this.grid,
52139             ed = g.activeEditor,
52140             forward = false;
52141         ///Roo.log('onEditorKey' + k);
52142         
52143         
52144         if (this.enter_is_tab && k == e.ENTER) {
52145             k = e.TAB;
52146         }
52147         
52148         if(k == e.TAB){
52149             if(e.shiftKey){
52150                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
52151             }else{
52152                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52153                 forward = true;
52154             }
52155             
52156             e.stopEvent();
52157             
52158         } else if(k == e.ENTER &&  !e.ctrlKey){
52159             ed.completeEdit();
52160             e.stopEvent();
52161             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
52162         
52163                 } else if(k == e.ESC){
52164             ed.cancelEdit();
52165         }
52166                 
52167         if (newCell) {
52168             var ecall = { cell : newCell, forward : forward };
52169             this.fireEvent('beforeeditnext', ecall );
52170             newCell = ecall.cell;
52171                         forward = ecall.forward;
52172         }
52173                 
52174         if(newCell){
52175             //Roo.log('next cell after edit');
52176             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
52177         } else if (forward) {
52178             // tabbed past last
52179             this.fireEvent.defer(100, this, ['tabend',this]);
52180         }
52181     }
52182 });/*
52183  * Based on:
52184  * Ext JS Library 1.1.1
52185  * Copyright(c) 2006-2007, Ext JS, LLC.
52186  *
52187  * Originally Released Under LGPL - original licence link has changed is not relivant.
52188  *
52189  * Fork - LGPL
52190  * <script type="text/javascript">
52191  */
52192  
52193 /**
52194  * @class Roo.grid.EditorGrid
52195  * @extends Roo.grid.Grid
52196  * Class for creating and editable grid.
52197  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
52198  * The container MUST have some type of size defined for the grid to fill. The container will be 
52199  * automatically set to position relative if it isn't already.
52200  * @param {Object} dataSource The data model to bind to
52201  * @param {Object} colModel The column model with info about this grid's columns
52202  */
52203 Roo.grid.EditorGrid = function(container, config){
52204     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
52205     this.getGridEl().addClass("xedit-grid");
52206
52207     if(!this.selModel){
52208         this.selModel = new Roo.grid.CellSelectionModel();
52209     }
52210
52211     this.activeEditor = null;
52212
52213         this.addEvents({
52214             /**
52215              * @event beforeedit
52216              * Fires before cell editing is triggered. The edit event object has the following properties <br />
52217              * <ul style="padding:5px;padding-left:16px;">
52218              * <li>grid - This grid</li>
52219              * <li>record - The record being edited</li>
52220              * <li>field - The field name being edited</li>
52221              * <li>value - The value for the field being edited.</li>
52222              * <li>row - The grid row index</li>
52223              * <li>column - The grid column index</li>
52224              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52225              * </ul>
52226              * @param {Object} e An edit event (see above for description)
52227              */
52228             "beforeedit" : true,
52229             /**
52230              * @event afteredit
52231              * Fires after a cell is edited. <br />
52232              * <ul style="padding:5px;padding-left:16px;">
52233              * <li>grid - This grid</li>
52234              * <li>record - The record being edited</li>
52235              * <li>field - The field name being edited</li>
52236              * <li>value - The value being set</li>
52237              * <li>originalValue - The original value for the field, before the edit.</li>
52238              * <li>row - The grid row index</li>
52239              * <li>column - The grid column index</li>
52240              * </ul>
52241              * @param {Object} e An edit event (see above for description)
52242              */
52243             "afteredit" : true,
52244             /**
52245              * @event validateedit
52246              * Fires after a cell is edited, but before the value is set in the record. 
52247          * You can use this to modify the value being set in the field, Return false
52248              * to cancel the change. The edit event object has the following properties <br />
52249              * <ul style="padding:5px;padding-left:16px;">
52250          * <li>editor - This editor</li>
52251              * <li>grid - This grid</li>
52252              * <li>record - The record being edited</li>
52253              * <li>field - The field name being edited</li>
52254              * <li>value - The value being set</li>
52255              * <li>originalValue - The original value for the field, before the edit.</li>
52256              * <li>row - The grid row index</li>
52257              * <li>column - The grid column index</li>
52258              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
52259              * </ul>
52260              * @param {Object} e An edit event (see above for description)
52261              */
52262             "validateedit" : true
52263         });
52264     this.on("bodyscroll", this.stopEditing,  this);
52265     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
52266 };
52267
52268 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
52269     /**
52270      * @cfg {Number} clicksToEdit
52271      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
52272      */
52273     clicksToEdit: 2,
52274
52275     // private
52276     isEditor : true,
52277     // private
52278     trackMouseOver: false, // causes very odd FF errors
52279
52280     onCellDblClick : function(g, row, col){
52281         this.startEditing(row, col);
52282     },
52283
52284     onEditComplete : function(ed, value, startValue){
52285         this.editing = false;
52286         this.activeEditor = null;
52287         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
52288         var r = ed.record;
52289         var field = this.colModel.getDataIndex(ed.col);
52290         var e = {
52291             grid: this,
52292             record: r,
52293             field: field,
52294             originalValue: startValue,
52295             value: value,
52296             row: ed.row,
52297             column: ed.col,
52298             cancel:false,
52299             editor: ed
52300         };
52301         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
52302         cell.show();
52303           
52304         if(String(value) !== String(startValue)){
52305             
52306             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
52307                 r.set(field, e.value);
52308                 // if we are dealing with a combo box..
52309                 // then we also set the 'name' colum to be the displayField
52310                 if (ed.field.displayField && ed.field.name) {
52311                     r.set(ed.field.name, ed.field.el.dom.value);
52312                 }
52313                 
52314                 delete e.cancel; //?? why!!!
52315                 this.fireEvent("afteredit", e);
52316             }
52317         } else {
52318             this.fireEvent("afteredit", e); // always fire it!
52319         }
52320         this.view.focusCell(ed.row, ed.col);
52321     },
52322
52323     /**
52324      * Starts editing the specified for the specified row/column
52325      * @param {Number} rowIndex
52326      * @param {Number} colIndex
52327      */
52328     startEditing : function(row, col){
52329         this.stopEditing();
52330         if(this.colModel.isCellEditable(col, row)){
52331             this.view.ensureVisible(row, col, true);
52332           
52333             var r = this.dataSource.getAt(row);
52334             var field = this.colModel.getDataIndex(col);
52335             var cell = Roo.get(this.view.getCell(row,col));
52336             var e = {
52337                 grid: this,
52338                 record: r,
52339                 field: field,
52340                 value: r.data[field],
52341                 row: row,
52342                 column: col,
52343                 cancel:false 
52344             };
52345             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
52346                 this.editing = true;
52347                 var ed = this.colModel.getCellEditor(col, row);
52348                 
52349                 if (!ed) {
52350                     return;
52351                 }
52352                 if(!ed.rendered){
52353                     ed.render(ed.parentEl || document.body);
52354                 }
52355                 ed.field.reset();
52356                
52357                 cell.hide();
52358                 
52359                 (function(){ // complex but required for focus issues in safari, ie and opera
52360                     ed.row = row;
52361                     ed.col = col;
52362                     ed.record = r;
52363                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
52364                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
52365                     this.activeEditor = ed;
52366                     var v = r.data[field];
52367                     ed.startEdit(this.view.getCell(row, col), v);
52368                     // combo's with 'displayField and name set
52369                     if (ed.field.displayField && ed.field.name) {
52370                         ed.field.el.dom.value = r.data[ed.field.name];
52371                     }
52372                     
52373                     
52374                 }).defer(50, this);
52375             }
52376         }
52377     },
52378         
52379     /**
52380      * Stops any active editing
52381      */
52382     stopEditing : function(){
52383         if(this.activeEditor){
52384             this.activeEditor.completeEdit();
52385         }
52386         this.activeEditor = null;
52387     }
52388 });/*
52389  * Based on:
52390  * Ext JS Library 1.1.1
52391  * Copyright(c) 2006-2007, Ext JS, LLC.
52392  *
52393  * Originally Released Under LGPL - original licence link has changed is not relivant.
52394  *
52395  * Fork - LGPL
52396  * <script type="text/javascript">
52397  */
52398
52399 // private - not really -- you end up using it !
52400 // This is a support class used internally by the Grid components
52401
52402 /**
52403  * @class Roo.grid.GridEditor
52404  * @extends Roo.Editor
52405  * Class for creating and editable grid elements.
52406  * @param {Object} config any settings (must include field)
52407  */
52408 Roo.grid.GridEditor = function(field, config){
52409     if (!config && field.field) {
52410         config = field;
52411         field = Roo.factory(config.field, Roo.form);
52412     }
52413     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
52414     field.monitorTab = false;
52415 };
52416
52417 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
52418     
52419     /**
52420      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
52421      */
52422     
52423     alignment: "tl-tl",
52424     autoSize: "width",
52425     hideEl : false,
52426     cls: "x-small-editor x-grid-editor",
52427     shim:false,
52428     shadow:"frame"
52429 });/*
52430  * Based on:
52431  * Ext JS Library 1.1.1
52432  * Copyright(c) 2006-2007, Ext JS, LLC.
52433  *
52434  * Originally Released Under LGPL - original licence link has changed is not relivant.
52435  *
52436  * Fork - LGPL
52437  * <script type="text/javascript">
52438  */
52439   
52440
52441   
52442 Roo.grid.PropertyRecord = Roo.data.Record.create([
52443     {name:'name',type:'string'},  'value'
52444 ]);
52445
52446
52447 Roo.grid.PropertyStore = function(grid, source){
52448     this.grid = grid;
52449     this.store = new Roo.data.Store({
52450         recordType : Roo.grid.PropertyRecord
52451     });
52452     this.store.on('update', this.onUpdate,  this);
52453     if(source){
52454         this.setSource(source);
52455     }
52456     Roo.grid.PropertyStore.superclass.constructor.call(this);
52457 };
52458
52459
52460
52461 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
52462     setSource : function(o){
52463         this.source = o;
52464         this.store.removeAll();
52465         var data = [];
52466         for(var k in o){
52467             if(this.isEditableValue(o[k])){
52468                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
52469             }
52470         }
52471         this.store.loadRecords({records: data}, {}, true);
52472     },
52473
52474     onUpdate : function(ds, record, type){
52475         if(type == Roo.data.Record.EDIT){
52476             var v = record.data['value'];
52477             var oldValue = record.modified['value'];
52478             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
52479                 this.source[record.id] = v;
52480                 record.commit();
52481                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
52482             }else{
52483                 record.reject();
52484             }
52485         }
52486     },
52487
52488     getProperty : function(row){
52489        return this.store.getAt(row);
52490     },
52491
52492     isEditableValue: function(val){
52493         if(val && val instanceof Date){
52494             return true;
52495         }else if(typeof val == 'object' || typeof val == 'function'){
52496             return false;
52497         }
52498         return true;
52499     },
52500
52501     setValue : function(prop, value){
52502         this.source[prop] = value;
52503         this.store.getById(prop).set('value', value);
52504     },
52505
52506     getSource : function(){
52507         return this.source;
52508     }
52509 });
52510
52511 Roo.grid.PropertyColumnModel = function(grid, store){
52512     this.grid = grid;
52513     var g = Roo.grid;
52514     g.PropertyColumnModel.superclass.constructor.call(this, [
52515         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
52516         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
52517     ]);
52518     this.store = store;
52519     this.bselect = Roo.DomHelper.append(document.body, {
52520         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
52521             {tag: 'option', value: 'true', html: 'true'},
52522             {tag: 'option', value: 'false', html: 'false'}
52523         ]
52524     });
52525     Roo.id(this.bselect);
52526     var f = Roo.form;
52527     this.editors = {
52528         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
52529         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
52530         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
52531         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
52532         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
52533     };
52534     this.renderCellDelegate = this.renderCell.createDelegate(this);
52535     this.renderPropDelegate = this.renderProp.createDelegate(this);
52536 };
52537
52538 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
52539     
52540     
52541     nameText : 'Name',
52542     valueText : 'Value',
52543     
52544     dateFormat : 'm/j/Y',
52545     
52546     
52547     renderDate : function(dateVal){
52548         return dateVal.dateFormat(this.dateFormat);
52549     },
52550
52551     renderBool : function(bVal){
52552         return bVal ? 'true' : 'false';
52553     },
52554
52555     isCellEditable : function(colIndex, rowIndex){
52556         return colIndex == 1;
52557     },
52558
52559     getRenderer : function(col){
52560         return col == 1 ?
52561             this.renderCellDelegate : this.renderPropDelegate;
52562     },
52563
52564     renderProp : function(v){
52565         return this.getPropertyName(v);
52566     },
52567
52568     renderCell : function(val){
52569         var rv = val;
52570         if(val instanceof Date){
52571             rv = this.renderDate(val);
52572         }else if(typeof val == 'boolean'){
52573             rv = this.renderBool(val);
52574         }
52575         return Roo.util.Format.htmlEncode(rv);
52576     },
52577
52578     getPropertyName : function(name){
52579         var pn = this.grid.propertyNames;
52580         return pn && pn[name] ? pn[name] : name;
52581     },
52582
52583     getCellEditor : function(colIndex, rowIndex){
52584         var p = this.store.getProperty(rowIndex);
52585         var n = p.data['name'], val = p.data['value'];
52586         
52587         if(typeof(this.grid.customEditors[n]) == 'string'){
52588             return this.editors[this.grid.customEditors[n]];
52589         }
52590         if(typeof(this.grid.customEditors[n]) != 'undefined'){
52591             return this.grid.customEditors[n];
52592         }
52593         if(val instanceof Date){
52594             return this.editors['date'];
52595         }else if(typeof val == 'number'){
52596             return this.editors['number'];
52597         }else if(typeof val == 'boolean'){
52598             return this.editors['boolean'];
52599         }else{
52600             return this.editors['string'];
52601         }
52602     }
52603 });
52604
52605 /**
52606  * @class Roo.grid.PropertyGrid
52607  * @extends Roo.grid.EditorGrid
52608  * This class represents the  interface of a component based property grid control.
52609  * <br><br>Usage:<pre><code>
52610  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52611       
52612  });
52613  // set any options
52614  grid.render();
52615  * </code></pre>
52616   
52617  * @constructor
52618  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52619  * The container MUST have some type of size defined for the grid to fill. The container will be
52620  * automatically set to position relative if it isn't already.
52621  * @param {Object} config A config object that sets properties on this grid.
52622  */
52623 Roo.grid.PropertyGrid = function(container, config){
52624     config = config || {};
52625     var store = new Roo.grid.PropertyStore(this);
52626     this.store = store;
52627     var cm = new Roo.grid.PropertyColumnModel(this, store);
52628     store.store.sort('name', 'ASC');
52629     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52630         ds: store.store,
52631         cm: cm,
52632         enableColLock:false,
52633         enableColumnMove:false,
52634         stripeRows:false,
52635         trackMouseOver: false,
52636         clicksToEdit:1
52637     }, config));
52638     this.getGridEl().addClass('x-props-grid');
52639     this.lastEditRow = null;
52640     this.on('columnresize', this.onColumnResize, this);
52641     this.addEvents({
52642          /**
52643              * @event beforepropertychange
52644              * Fires before a property changes (return false to stop?)
52645              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52646              * @param {String} id Record Id
52647              * @param {String} newval New Value
52648          * @param {String} oldval Old Value
52649              */
52650         "beforepropertychange": true,
52651         /**
52652              * @event propertychange
52653              * Fires after a property changes
52654              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52655              * @param {String} id Record Id
52656              * @param {String} newval New Value
52657          * @param {String} oldval Old Value
52658              */
52659         "propertychange": true
52660     });
52661     this.customEditors = this.customEditors || {};
52662 };
52663 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52664     
52665      /**
52666      * @cfg {Object} customEditors map of colnames=> custom editors.
52667      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52668      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52669      * false disables editing of the field.
52670          */
52671     
52672       /**
52673      * @cfg {Object} propertyNames map of property Names to their displayed value
52674          */
52675     
52676     render : function(){
52677         Roo.grid.PropertyGrid.superclass.render.call(this);
52678         this.autoSize.defer(100, this);
52679     },
52680
52681     autoSize : function(){
52682         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52683         if(this.view){
52684             this.view.fitColumns();
52685         }
52686     },
52687
52688     onColumnResize : function(){
52689         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52690         this.autoSize();
52691     },
52692     /**
52693      * Sets the data for the Grid
52694      * accepts a Key => Value object of all the elements avaiable.
52695      * @param {Object} data  to appear in grid.
52696      */
52697     setSource : function(source){
52698         this.store.setSource(source);
52699         //this.autoSize();
52700     },
52701     /**
52702      * Gets all the data from the grid.
52703      * @return {Object} data  data stored in grid
52704      */
52705     getSource : function(){
52706         return this.store.getSource();
52707     }
52708 });/*
52709  * Based on:
52710  * Ext JS Library 1.1.1
52711  * Copyright(c) 2006-2007, Ext JS, LLC.
52712  *
52713  * Originally Released Under LGPL - original licence link has changed is not relivant.
52714  *
52715  * Fork - LGPL
52716  * <script type="text/javascript">
52717  */
52718  
52719 /**
52720  * @class Roo.LoadMask
52721  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52722  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52723  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52724  * element's UpdateManager load indicator and will be destroyed after the initial load.
52725  * @constructor
52726  * Create a new LoadMask
52727  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52728  * @param {Object} config The config object
52729  */
52730 Roo.LoadMask = function(el, config){
52731     this.el = Roo.get(el);
52732     Roo.apply(this, config);
52733     if(this.store){
52734         this.store.on('beforeload', this.onBeforeLoad, this);
52735         this.store.on('load', this.onLoad, this);
52736         this.store.on('loadexception', this.onLoadException, this);
52737         this.removeMask = false;
52738     }else{
52739         var um = this.el.getUpdateManager();
52740         um.showLoadIndicator = false; // disable the default indicator
52741         um.on('beforeupdate', this.onBeforeLoad, this);
52742         um.on('update', this.onLoad, this);
52743         um.on('failure', this.onLoad, this);
52744         this.removeMask = true;
52745     }
52746 };
52747
52748 Roo.LoadMask.prototype = {
52749     /**
52750      * @cfg {Boolean} removeMask
52751      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52752      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52753      */
52754     /**
52755      * @cfg {String} msg
52756      * The text to display in a centered loading message box (defaults to 'Loading...')
52757      */
52758     msg : 'Loading...',
52759     /**
52760      * @cfg {String} msgCls
52761      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52762      */
52763     msgCls : 'x-mask-loading',
52764
52765     /**
52766      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52767      * @type Boolean
52768      */
52769     disabled: false,
52770
52771     /**
52772      * Disables the mask to prevent it from being displayed
52773      */
52774     disable : function(){
52775        this.disabled = true;
52776     },
52777
52778     /**
52779      * Enables the mask so that it can be displayed
52780      */
52781     enable : function(){
52782         this.disabled = false;
52783     },
52784     
52785     onLoadException : function()
52786     {
52787         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52788             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52789         }
52790         this.el.unmask(this.removeMask);
52791     },
52792     // private
52793     onLoad : function()
52794     {
52795         this.el.unmask(this.removeMask);
52796     },
52797
52798     // private
52799     onBeforeLoad : function(){
52800         if(!this.disabled){
52801             this.el.mask(this.msg, this.msgCls);
52802         }
52803     },
52804
52805     // private
52806     destroy : function(){
52807         if(this.store){
52808             this.store.un('beforeload', this.onBeforeLoad, this);
52809             this.store.un('load', this.onLoad, this);
52810             this.store.un('loadexception', this.onLoadException, this);
52811         }else{
52812             var um = this.el.getUpdateManager();
52813             um.un('beforeupdate', this.onBeforeLoad, this);
52814             um.un('update', this.onLoad, this);
52815             um.un('failure', this.onLoad, this);
52816         }
52817     }
52818 };/*
52819  * Based on:
52820  * Ext JS Library 1.1.1
52821  * Copyright(c) 2006-2007, Ext JS, LLC.
52822  *
52823  * Originally Released Under LGPL - original licence link has changed is not relivant.
52824  *
52825  * Fork - LGPL
52826  * <script type="text/javascript">
52827  */
52828
52829
52830 /**
52831  * @class Roo.XTemplate
52832  * @extends Roo.Template
52833  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
52834 <pre><code>
52835 var t = new Roo.XTemplate(
52836         '&lt;select name="{name}"&gt;',
52837                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
52838         '&lt;/select&gt;'
52839 );
52840  
52841 // then append, applying the master template values
52842  </code></pre>
52843  *
52844  * Supported features:
52845  *
52846  *  Tags:
52847
52848 <pre><code>
52849       {a_variable} - output encoded.
52850       {a_variable.format:("Y-m-d")} - call a method on the variable
52851       {a_variable:raw} - unencoded output
52852       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
52853       {a_variable:this.method_on_template(...)} - call a method on the template object.
52854  
52855 </code></pre>
52856  *  The tpl tag:
52857 <pre><code>
52858         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
52859         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
52860         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
52861         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
52862   
52863         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
52864         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
52865 </code></pre>
52866  *      
52867  */
52868 Roo.XTemplate = function()
52869 {
52870     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52871     if (this.html) {
52872         this.compile();
52873     }
52874 };
52875
52876
52877 Roo.extend(Roo.XTemplate, Roo.Template, {
52878
52879     /**
52880      * The various sub templates
52881      */
52882     tpls : false,
52883     /**
52884      *
52885      * basic tag replacing syntax
52886      * WORD:WORD()
52887      *
52888      * // you can fake an object call by doing this
52889      *  x.t:(test,tesT) 
52890      * 
52891      */
52892     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52893
52894     /**
52895      * compile the template
52896      *
52897      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
52898      *
52899      */
52900     compile: function()
52901     {
52902         var s = this.html;
52903      
52904         s = ['<tpl>', s, '</tpl>'].join('');
52905     
52906         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
52907             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
52908             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
52909             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
52910             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
52911             m,
52912             id     = 0,
52913             tpls   = [];
52914     
52915         while(true == !!(m = s.match(re))){
52916             var forMatch   = m[0].match(nameRe),
52917                 ifMatch   = m[0].match(ifRe),
52918                 execMatch   = m[0].match(execRe),
52919                 namedMatch   = m[0].match(namedRe),
52920                 
52921                 exp  = null, 
52922                 fn   = null,
52923                 exec = null,
52924                 name = forMatch && forMatch[1] ? forMatch[1] : '';
52925                 
52926             if (ifMatch) {
52927                 // if - puts fn into test..
52928                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
52929                 if(exp){
52930                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52931                 }
52932             }
52933             
52934             if (execMatch) {
52935                 // exec - calls a function... returns empty if true is  returned.
52936                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
52937                 if(exp){
52938                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52939                 }
52940             }
52941             
52942             
52943             if (name) {
52944                 // for = 
52945                 switch(name){
52946                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52947                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52948                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52949                 }
52950             }
52951             var uid = namedMatch ? namedMatch[1] : id;
52952             
52953             
52954             tpls.push({
52955                 id:     namedMatch ? namedMatch[1] : id,
52956                 target: name,
52957                 exec:   exec,
52958                 test:   fn,
52959                 body:   m[1] || ''
52960             });
52961             if (namedMatch) {
52962                 s = s.replace(m[0], '');
52963             } else { 
52964                 s = s.replace(m[0], '{xtpl'+ id + '}');
52965             }
52966             ++id;
52967         }
52968         this.tpls = [];
52969         for(var i = tpls.length-1; i >= 0; --i){
52970             this.compileTpl(tpls[i]);
52971             this.tpls[tpls[i].id] = tpls[i];
52972         }
52973         this.master = tpls[tpls.length-1];
52974         return this;
52975     },
52976     /**
52977      * same as applyTemplate, except it's done to one of the subTemplates
52978      * when using named templates, you can do:
52979      *
52980      * var str = pl.applySubTemplate('your-name', values);
52981      *
52982      * 
52983      * @param {Number} id of the template
52984      * @param {Object} values to apply to template
52985      * @param {Object} parent (normaly the instance of this object)
52986      */
52987     applySubTemplate : function(id, values, parent)
52988     {
52989         
52990         
52991         var t = this.tpls[id];
52992         
52993         
52994         try { 
52995             if(t.test && !t.test.call(this, values, parent)){
52996                 return '';
52997             }
52998         } catch(e) {
52999             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
53000             Roo.log(e.toString());
53001             Roo.log(t.test);
53002             return ''
53003         }
53004         try { 
53005             
53006             if(t.exec && t.exec.call(this, values, parent)){
53007                 return '';
53008             }
53009         } catch(e) {
53010             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
53011             Roo.log(e.toString());
53012             Roo.log(t.exec);
53013             return ''
53014         }
53015         try {
53016             var vs = t.target ? t.target.call(this, values, parent) : values;
53017             parent = t.target ? values : parent;
53018             if(t.target && vs instanceof Array){
53019                 var buf = [];
53020                 for(var i = 0, len = vs.length; i < len; i++){
53021                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
53022                 }
53023                 return buf.join('');
53024             }
53025             return t.compiled.call(this, vs, parent);
53026         } catch (e) {
53027             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
53028             Roo.log(e.toString());
53029             Roo.log(t.compiled);
53030             return '';
53031         }
53032     },
53033
53034     compileTpl : function(tpl)
53035     {
53036         var fm = Roo.util.Format;
53037         var useF = this.disableFormats !== true;
53038         var sep = Roo.isGecko ? "+" : ",";
53039         var undef = function(str) {
53040             Roo.log("Property not found :"  + str);
53041             return '';
53042         };
53043         
53044         var fn = function(m, name, format, args)
53045         {
53046             //Roo.log(arguments);
53047             args = args ? args.replace(/\\'/g,"'") : args;
53048             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
53049             if (typeof(format) == 'undefined') {
53050                 format= 'htmlEncode';
53051             }
53052             if (format == 'raw' ) {
53053                 format = false;
53054             }
53055             
53056             if(name.substr(0, 4) == 'xtpl'){
53057                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
53058             }
53059             
53060             // build an array of options to determine if value is undefined..
53061             
53062             // basically get 'xxxx.yyyy' then do
53063             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
53064             //    (function () { Roo.log("Property not found"); return ''; })() :
53065             //    ......
53066             
53067             var udef_ar = [];
53068             var lookfor = '';
53069             Roo.each(name.split('.'), function(st) {
53070                 lookfor += (lookfor.length ? '.': '') + st;
53071                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
53072             });
53073             
53074             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
53075             
53076             
53077             if(format && useF){
53078                 
53079                 args = args ? ',' + args : "";
53080                  
53081                 if(format.substr(0, 5) != "this."){
53082                     format = "fm." + format + '(';
53083                 }else{
53084                     format = 'this.call("'+ format.substr(5) + '", ';
53085                     args = ", values";
53086                 }
53087                 
53088                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
53089             }
53090              
53091             if (args.length) {
53092                 // called with xxyx.yuu:(test,test)
53093                 // change to ()
53094                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
53095             }
53096             // raw.. - :raw modifier..
53097             return "'"+ sep + udef_st  + name + ")"+sep+"'";
53098             
53099         };
53100         var body;
53101         // branched to use + in gecko and [].join() in others
53102         if(Roo.isGecko){
53103             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
53104                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
53105                     "';};};";
53106         }else{
53107             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
53108             body.push(tpl.body.replace(/(\r\n|\n)/g,
53109                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
53110             body.push("'].join('');};};");
53111             body = body.join('');
53112         }
53113         
53114         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
53115        
53116         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
53117         eval(body);
53118         
53119         return this;
53120     },
53121
53122     applyTemplate : function(values){
53123         return this.master.compiled.call(this, values, {});
53124         //var s = this.subs;
53125     },
53126
53127     apply : function(){
53128         return this.applyTemplate.apply(this, arguments);
53129     }
53130
53131  });
53132
53133 Roo.XTemplate.from = function(el){
53134     el = Roo.getDom(el);
53135     return new Roo.XTemplate(el.value || el.innerHTML);
53136 };/*
53137  * Original code for Roojs - LGPL
53138  * <script type="text/javascript">
53139  */
53140  
53141 /**
53142  * @class Roo.XComponent
53143  * A delayed Element creator...
53144  * Or a way to group chunks of interface together.
53145  * 
53146  * Mypart.xyx = new Roo.XComponent({
53147
53148     parent : 'Mypart.xyz', // empty == document.element.!!
53149     order : '001',
53150     name : 'xxxx'
53151     region : 'xxxx'
53152     disabled : function() {} 
53153      
53154     tree : function() { // return an tree of xtype declared components
53155         var MODULE = this;
53156         return 
53157         {
53158             xtype : 'NestedLayoutPanel',
53159             // technicall
53160         }
53161      ]
53162  *})
53163  *
53164  *
53165  * It can be used to build a big heiracy, with parent etc.
53166  * or you can just use this to render a single compoent to a dom element
53167  * MYPART.render(Roo.Element | String(id) | dom_element )
53168  * 
53169  * @extends Roo.util.Observable
53170  * @constructor
53171  * @param cfg {Object} configuration of component
53172  * 
53173  */
53174 Roo.XComponent = function(cfg) {
53175     Roo.apply(this, cfg);
53176     this.addEvents({ 
53177         /**
53178              * @event built
53179              * Fires when this the componnt is built
53180              * @param {Roo.XComponent} c the component
53181              */
53182         'built' : true
53183         
53184     });
53185     this.region = this.region || 'center'; // default..
53186     Roo.XComponent.register(this);
53187     this.modules = false;
53188     this.el = false; // where the layout goes..
53189     
53190     
53191 }
53192 Roo.extend(Roo.XComponent, Roo.util.Observable, {
53193     /**
53194      * @property el
53195      * The created element (with Roo.factory())
53196      * @type {Roo.Layout}
53197      */
53198     el  : false,
53199     
53200     /**
53201      * @property el
53202      * for BC  - use el in new code
53203      * @type {Roo.Layout}
53204      */
53205     panel : false,
53206     
53207     /**
53208      * @property layout
53209      * for BC  - use el in new code
53210      * @type {Roo.Layout}
53211      */
53212     layout : false,
53213     
53214      /**
53215      * @cfg {Function|boolean} disabled
53216      * If this module is disabled by some rule, return true from the funtion
53217      */
53218     disabled : false,
53219     
53220     /**
53221      * @cfg {String} parent 
53222      * Name of parent element which it get xtype added to..
53223      */
53224     parent: false,
53225     
53226     /**
53227      * @cfg {String} order
53228      * Used to set the order in which elements are created (usefull for multiple tabs)
53229      */
53230     
53231     order : false,
53232     /**
53233      * @cfg {String} name
53234      * String to display while loading.
53235      */
53236     name : false,
53237     /**
53238      * @cfg {String} region
53239      * Region to render component to (defaults to center)
53240      */
53241     region : 'center',
53242     
53243     /**
53244      * @cfg {Array} items
53245      * A single item array - the first element is the root of the tree..
53246      * It's done this way to stay compatible with the Xtype system...
53247      */
53248     items : false,
53249     
53250     /**
53251      * @property _tree
53252      * The method that retuns the tree of parts that make up this compoennt 
53253      * @type {function}
53254      */
53255     _tree  : false,
53256     
53257      /**
53258      * render
53259      * render element to dom or tree
53260      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
53261      */
53262     
53263     render : function(el)
53264     {
53265         
53266         el = el || false;
53267         var hp = this.parent ? 1 : 0;
53268         
53269         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
53270             // if parent is a '#.....' string, then let's use that..
53271             var ename = this.parent.substr(1)
53272             this.parent = false;
53273             el = Roo.get(ename);
53274             if (!el) {
53275                 Roo.log("Warning - element can not be found :#" + ename );
53276                 return;
53277             }
53278         }
53279         
53280         
53281         if (!this.parent) {
53282             
53283             el = el ? Roo.get(el) : false;      
53284             
53285             // it's a top level one..
53286             this.parent =  {
53287                 el : new Roo.BorderLayout(el || document.body, {
53288                 
53289                      center: {
53290                          titlebar: false,
53291                          autoScroll:false,
53292                          closeOnTab: true,
53293                          tabPosition: 'top',
53294                           //resizeTabs: true,
53295                          alwaysShowTabs: el && hp? false :  true,
53296                          hideTabs: el || !hp ? true :  false,
53297                          minTabWidth: 140
53298                      }
53299                  })
53300             }
53301         }
53302         
53303                 if (!this.parent.el) {
53304                         // probably an old style ctor, which has been disabled.
53305                         return;
53306                         
53307                 }
53308                 // The 'tree' method is  '_tree now' 
53309             
53310         var tree = this._tree ? this._tree() : this.tree();
53311         tree.region = tree.region || this.region;
53312         this.el = this.parent.el.addxtype(tree);
53313         this.fireEvent('built', this);
53314         
53315         this.panel = this.el;
53316         this.layout = this.panel.layout;
53317                 this.parentLayout = this.parent.layout  || false;  
53318          
53319     }
53320     
53321 });
53322
53323 Roo.apply(Roo.XComponent, {
53324     /**
53325      * @property  hideProgress
53326      * true to disable the building progress bar.. usefull on single page renders.
53327      * @type Boolean
53328      */
53329     hideProgress : false,
53330     /**
53331      * @property  buildCompleted
53332      * True when the builder has completed building the interface.
53333      * @type Boolean
53334      */
53335     buildCompleted : false,
53336      
53337     /**
53338      * @property  topModule
53339      * the upper most module - uses document.element as it's constructor.
53340      * @type Object
53341      */
53342      
53343     topModule  : false,
53344       
53345     /**
53346      * @property  modules
53347      * array of modules to be created by registration system.
53348      * @type {Array} of Roo.XComponent
53349      */
53350     
53351     modules : [],
53352     /**
53353      * @property  elmodules
53354      * array of modules to be created by which use #ID 
53355      * @type {Array} of Roo.XComponent
53356      */
53357      
53358     elmodules : [],
53359
53360     
53361     /**
53362      * Register components to be built later.
53363      *
53364      * This solves the following issues
53365      * - Building is not done on page load, but after an authentication process has occured.
53366      * - Interface elements are registered on page load
53367      * - Parent Interface elements may not be loaded before child, so this handles that..
53368      * 
53369      *
53370      * example:
53371      * 
53372      * MyApp.register({
53373           order : '000001',
53374           module : 'Pman.Tab.projectMgr',
53375           region : 'center',
53376           parent : 'Pman.layout',
53377           disabled : false,  // or use a function..
53378         })
53379      
53380      * * @param {Object} details about module
53381      */
53382     register : function(obj) {
53383                 
53384         Roo.XComponent.event.fireEvent('register', obj);
53385         switch(typeof(obj.disabled) ) {
53386                 
53387             case 'undefined':
53388                 break;
53389             
53390             case 'function':
53391                 if ( obj.disabled() ) {
53392                         return;
53393                 }
53394                 break;
53395             
53396             default:
53397                 if (obj.disabled) {
53398                         return;
53399                 }
53400                 break;
53401         }
53402                 
53403         this.modules.push(obj);
53404          
53405     },
53406     /**
53407      * convert a string to an object..
53408      * eg. 'AAA.BBB' -> finds AAA.BBB
53409
53410      */
53411     
53412     toObject : function(str)
53413     {
53414         if (!str || typeof(str) == 'object') {
53415             return str;
53416         }
53417         if (str.substring(0,1) == '#') {
53418             return str;
53419         }
53420
53421         var ar = str.split('.');
53422         var rt, o;
53423         rt = ar.shift();
53424             /** eval:var:o */
53425         try {
53426             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
53427         } catch (e) {
53428             throw "Module not found : " + str;
53429         }
53430         
53431         if (o === false) {
53432             throw "Module not found : " + str;
53433         }
53434         Roo.each(ar, function(e) {
53435             if (typeof(o[e]) == 'undefined') {
53436                 throw "Module not found : " + str;
53437             }
53438             o = o[e];
53439         });
53440         
53441         return o;
53442         
53443     },
53444     
53445     
53446     /**
53447      * move modules into their correct place in the tree..
53448      * 
53449      */
53450     preBuild : function ()
53451     {
53452         var _t = this;
53453         Roo.each(this.modules , function (obj)
53454         {
53455             Roo.XComponent.event.fireEvent('beforebuild', obj);
53456             
53457             var opar = obj.parent;
53458             try { 
53459                 obj.parent = this.toObject(opar);
53460             } catch(e) {
53461                 Roo.log("parent:toObject failed: " + e.toString());
53462                 return;
53463             }
53464             
53465             if (!obj.parent) {
53466                 Roo.debug && Roo.log("GOT top level module");
53467                 Roo.debug && Roo.log(obj);
53468                 obj.modules = new Roo.util.MixedCollection(false, 
53469                     function(o) { return o.order + '' }
53470                 );
53471                 this.topModule = obj;
53472                 return;
53473             }
53474                         // parent is a string (usually a dom element name..)
53475             if (typeof(obj.parent) == 'string') {
53476                 this.elmodules.push(obj);
53477                 return;
53478             }
53479             if (obj.parent.constructor != Roo.XComponent) {
53480                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
53481             }
53482             if (!obj.parent.modules) {
53483                 obj.parent.modules = new Roo.util.MixedCollection(false, 
53484                     function(o) { return o.order + '' }
53485                 );
53486             }
53487             if (obj.parent.disabled) {
53488                 obj.disabled = true;
53489             }
53490             obj.parent.modules.add(obj);
53491         }, this);
53492     },
53493     
53494      /**
53495      * make a list of modules to build.
53496      * @return {Array} list of modules. 
53497      */ 
53498     
53499     buildOrder : function()
53500     {
53501         var _this = this;
53502         var cmp = function(a,b) {   
53503             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
53504         };
53505         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
53506             throw "No top level modules to build";
53507         }
53508         
53509         // make a flat list in order of modules to build.
53510         var mods = this.topModule ? [ this.topModule ] : [];
53511                 
53512         // elmodules (is a list of DOM based modules )
53513         Roo.each(this.elmodules, function(e) {
53514             mods.push(e)
53515         });
53516
53517         
53518         // add modules to their parents..
53519         var addMod = function(m) {
53520             Roo.debug && Roo.log("build Order: add: " + m.name);
53521             
53522         mods.push(m);
53523         if (m.modules && !m.disabled) {
53524             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
53525             m.modules.keySort('ASC',  cmp );
53526             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
53527
53528             m.modules.each(addMod);
53529         } else {
53530             Roo.debug && Roo.log("build Order: no child modules");
53531             }
53532             // not sure if this is used any more..
53533             if (m.finalize) {
53534                 m.finalize.name = m.name + " (clean up) ";
53535                 mods.push(m.finalize);
53536             }
53537             
53538         }
53539         if (this.topModule) { 
53540             this.topModule.modules.keySort('ASC',  cmp );
53541             this.topModule.modules.each(addMod);
53542         }
53543         return mods;
53544     },
53545     
53546      /**
53547      * Build the registered modules.
53548      * @param {Object} parent element.
53549      * @param {Function} optional method to call after module has been added.
53550      * 
53551      */ 
53552    
53553     build : function() 
53554     {
53555         
53556         this.preBuild();
53557         var mods = this.buildOrder();
53558       
53559         //this.allmods = mods;
53560         //Roo.debug && Roo.log(mods);
53561         //return;
53562         if (!mods.length) { // should not happen
53563             throw "NO modules!!!";
53564         }
53565         
53566         
53567         var msg = "Building Interface...";
53568         // flash it up as modal - so we store the mask!?
53569         if (!this.hideProgress) {
53570             Roo.MessageBox.show({ title: 'loading' });
53571             Roo.MessageBox.show({
53572                title: "Please wait...",
53573                msg: msg,
53574                width:450,
53575                progress:true,
53576                closable:false,
53577                modal: false
53578               
53579             });
53580         }
53581         var total = mods.length;
53582         
53583         var _this = this;
53584         var progressRun = function() {
53585             if (!mods.length) {
53586                 Roo.debug && Roo.log('hide?');
53587                 if (!this.hideProgress) {
53588                     Roo.MessageBox.hide();
53589                 }
53590                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
53591                 
53592                 // THE END...
53593                 return false;   
53594             }
53595             
53596             var m = mods.shift();
53597             
53598             
53599             Roo.debug && Roo.log(m);
53600             // not sure if this is supported any more.. - modules that are are just function
53601             if (typeof(m) == 'function') { 
53602                 m.call(this);
53603                 return progressRun.defer(10, _this);
53604             } 
53605             
53606             
53607             msg = "Building Interface " + (total  - mods.length) + 
53608                     " of " + total + 
53609                     (m.name ? (' - ' + m.name) : '');
53610                         Roo.debug && Roo.log(msg);
53611             if (!this.hideProgress) { 
53612                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
53613             }
53614             
53615          
53616             // is the module disabled?
53617             var disabled = (typeof(m.disabled) == 'function') ?
53618                 m.disabled.call(m.module.disabled) : m.disabled;    
53619             
53620             
53621             if (disabled) {
53622                 return progressRun(); // we do not update the display!
53623             }
53624             
53625             // now build 
53626             
53627                         
53628                         
53629             m.render();
53630             // it's 10 on top level, and 1 on others??? why...
53631             return progressRun.defer(10, _this);
53632              
53633         }
53634         progressRun.defer(1, _this);
53635      
53636         
53637         
53638     },
53639         
53640         
53641         /**
53642          * Event Object.
53643          *
53644          *
53645          */
53646         event: false, 
53647     /**
53648          * wrapper for event.on - aliased later..  
53649          * Typically use to register a event handler for register:
53650          *
53651          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
53652          *
53653          */
53654     on : false
53655    
53656     
53657     
53658 });
53659
53660 Roo.XComponent.event = new Roo.util.Observable({
53661                 events : { 
53662                         /**
53663                          * @event register
53664                          * Fires when an Component is registered,
53665                          * set the disable property on the Component to stop registration.
53666                          * @param {Roo.XComponent} c the component being registerd.
53667                          * 
53668                          */
53669                         'register' : true,
53670             /**
53671                          * @event beforebuild
53672                          * Fires before each Component is built
53673                          * can be used to apply permissions.
53674                          * @param {Roo.XComponent} c the component being registerd.
53675                          * 
53676                          */
53677                         'beforebuild' : true,
53678                         /**
53679                          * @event buildcomplete
53680                          * Fires on the top level element when all elements have been built
53681                          * @param {Roo.XComponent} the top level component.
53682                          */
53683                         'buildcomplete' : true
53684                         
53685                 }
53686 });
53687
53688 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
53689  //<script type="text/javascript">
53690
53691
53692 /**
53693  * @class Roo.Login
53694  * @extends Roo.LayoutDialog
53695  * A generic Login Dialog..... - only one needed in theory!?!?
53696  *
53697  * Fires XComponent builder on success...
53698  * 
53699  * Sends 
53700  *    username,password, lang = for login actions.
53701  *    check = 1 for periodic checking that sesion is valid.
53702  *    passwordRequest = email request password
53703  *    logout = 1 = to logout
53704  * 
53705  * Affects: (this id="????" elements)
53706  *   loading  (removed) (used to indicate application is loading)
53707  *   loading-mask (hides) (used to hide application when it's building loading)
53708  *   
53709  * 
53710  * Usage: 
53711  *    
53712  * 
53713  * Myapp.login = Roo.Login({
53714      url: xxxx,
53715    
53716      realm : 'Myapp', 
53717      
53718      
53719      method : 'POST',
53720      
53721      
53722      * 
53723  })
53724  * 
53725  * 
53726  * 
53727  **/
53728  
53729 Roo.Login = function(cfg)
53730 {
53731     this.addEvents({
53732         'refreshed' : true
53733     });
53734     
53735     Roo.apply(this,cfg);
53736     
53737     Roo.onReady(function() {
53738         this.onLoad();
53739     }, this);
53740     // call parent..
53741     
53742    
53743     Roo.Login.superclass.constructor.call(this, this);
53744     //this.addxtype(this.items[0]);
53745     
53746     
53747 }
53748
53749
53750 Roo.extend(Roo.Login, Roo.LayoutDialog, {
53751     
53752     /**
53753      * @cfg {String} method
53754      * Method used to query for login details.
53755      */
53756     
53757     method : 'POST',
53758     /**
53759      * @cfg {String} url
53760      * URL to query login data. - eg. baseURL + '/Login.php'
53761      */
53762     url : '',
53763     
53764     /**
53765      * @property user
53766      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
53767      * @type {Object} 
53768      */
53769     user : false,
53770     /**
53771      * @property checkFails
53772      * Number of times we have attempted to get authentication check, and failed.
53773      * @type {Number} 
53774      */
53775     checkFails : 0,
53776       /**
53777      * @property intervalID
53778      * The window interval that does the constant login checking.
53779      * @type {Number} 
53780      */
53781     intervalID : 0,
53782     
53783     
53784     onLoad : function() // called on page load...
53785     {
53786         // load 
53787          
53788         if (Roo.get('loading')) { // clear any loading indicator..
53789             Roo.get('loading').remove();
53790         }
53791         
53792         //this.switchLang('en'); // set the language to english..
53793        
53794         this.check({
53795             success:  function(response, opts)  {  // check successfull...
53796             
53797                 var res = this.processResponse(response);
53798                 this.checkFails =0;
53799                 if (!res.success) { // error!
53800                     this.checkFails = 5;
53801                     //console.log('call failure');
53802                     return this.failure(response,opts);
53803                 }
53804                 
53805                 if (!res.data.id) { // id=0 == login failure.
53806                     return this.show();
53807                 }
53808                 
53809                               
53810                         //console.log(success);
53811                 this.fillAuth(res.data);   
53812                 this.checkFails =0;
53813                 Roo.XComponent.build();
53814             },
53815             failure : this.show
53816         });
53817         
53818     }, 
53819     
53820     
53821     check: function(cfg) // called every so often to refresh cookie etc..
53822     {
53823         if (cfg.again) { // could be undefined..
53824             this.checkFails++;
53825         } else {
53826             this.checkFails = 0;
53827         }
53828         var _this = this;
53829         if (this.sending) {
53830             if ( this.checkFails > 4) {
53831                 Roo.MessageBox.alert("Error",  
53832                     "Error getting authentication status. - try reloading, or wait a while", function() {
53833                         _this.sending = false;
53834                     }); 
53835                 return;
53836             }
53837             cfg.again = true;
53838             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
53839             return;
53840         }
53841         this.sending = true;
53842         
53843         Roo.Ajax.request({  
53844             url: this.url,
53845             params: {
53846                 getAuthUser: true
53847             },  
53848             method: this.method,
53849             success:  cfg.success || this.success,
53850             failure : cfg.failure || this.failure,
53851             scope : this,
53852             callCfg : cfg
53853               
53854         });  
53855     }, 
53856     
53857     
53858     logout: function()
53859     {
53860         window.onbeforeunload = function() { }; // false does not work for IE..
53861         this.user = false;
53862         var _this = this;
53863         
53864         Roo.Ajax.request({  
53865             url: this.url,
53866             params: {
53867                 logout: 1
53868             },  
53869             method: 'GET',
53870             failure : function() {
53871                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53872                     document.location = document.location.toString() + '?ts=' + Math.random();
53873                 });
53874                 
53875             },
53876             success : function() {
53877                 _this.user = false;
53878                 this.checkFails =0;
53879                 // fixme..
53880                 document.location = document.location.toString() + '?ts=' + Math.random();
53881             }
53882               
53883               
53884         }); 
53885     },
53886     
53887     processResponse : function (response)
53888     {
53889         var res = '';
53890         try {
53891             res = Roo.decode(response.responseText);
53892             // oops...
53893             if (typeof(res) != 'object') {
53894                 res = { success : false, errorMsg : res, errors : true };
53895             }
53896             if (typeof(res.success) == 'undefined') {
53897                 res.success = false;
53898             }
53899             
53900         } catch(e) {
53901             res = { success : false,  errorMsg : response.responseText, errors : true };
53902         }
53903         return res;
53904     },
53905     
53906     success : function(response, opts)  // check successfull...
53907     {  
53908         this.sending = false;
53909         var res = this.processResponse(response);
53910         if (!res.success) {
53911             return this.failure(response, opts);
53912         }
53913         if (!res.data || !res.data.id) {
53914             return this.failure(response,opts);
53915         }
53916         //console.log(res);
53917         this.fillAuth(res.data);
53918         
53919         this.checkFails =0;
53920         
53921     },
53922     
53923     
53924     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53925     {
53926         this.authUser = -1;
53927         this.sending = false;
53928         var res = this.processResponse(response);
53929         //console.log(res);
53930         if ( this.checkFails > 2) {
53931         
53932             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53933                 "Error getting authentication status. - try reloading"); 
53934             return;
53935         }
53936         opts.callCfg.again = true;
53937         this.check.defer(1000, this, [ opts.callCfg ]);
53938         return;  
53939     },
53940     
53941     
53942     
53943     fillAuth: function(au) {
53944         this.startAuthCheck();
53945         this.authUserId = au.id;
53946         this.authUser = au;
53947         this.lastChecked = new Date();
53948         this.fireEvent('refreshed', au);
53949         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53950         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53951         au.lang = au.lang || 'en';
53952         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53953         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53954         this.switchLang(au.lang );
53955         
53956      
53957         // open system... - -on setyp..
53958         if (this.authUserId  < 0) {
53959             Roo.MessageBox.alert("Warning", 
53960                 "This is an open system - please set up a admin user with a password.");  
53961         }
53962          
53963         //Pman.onload(); // which should do nothing if it's a re-auth result...
53964         
53965              
53966     },
53967     
53968     startAuthCheck : function() // starter for timeout checking..
53969     {
53970         if (this.intervalID) { // timer already in place...
53971             return false;
53972         }
53973         var _this = this;
53974         this.intervalID =  window.setInterval(function() {
53975               _this.check(false);
53976             }, 120000); // every 120 secs = 2mins..
53977         
53978         
53979     },
53980          
53981     
53982     switchLang : function (lang) 
53983     {
53984         _T = typeof(_T) == 'undefined' ? false : _T;
53985           if (!_T || !lang.length) {
53986             return;
53987         }
53988         
53989         if (!_T && lang != 'en') {
53990             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53991             return;
53992         }
53993         
53994         if (typeof(_T.en) == 'undefined') {
53995             _T.en = {};
53996             Roo.apply(_T.en, _T);
53997         }
53998         
53999         if (typeof(_T[lang]) == 'undefined') {
54000             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
54001             return;
54002         }
54003         
54004         
54005         Roo.apply(_T, _T[lang]);
54006         // just need to set the text values for everything...
54007         var _this = this;
54008         /* this will not work ...
54009         if (this.form) { 
54010             
54011                
54012             function formLabel(name, val) {
54013                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
54014             }
54015             
54016             formLabel('password', "Password"+':');
54017             formLabel('username', "Email Address"+':');
54018             formLabel('lang', "Language"+':');
54019             this.dialog.setTitle("Login");
54020             this.dialog.buttons[0].setText("Forgot Password");
54021             this.dialog.buttons[1].setText("Login");
54022         }
54023         */
54024         
54025         
54026     },
54027     
54028     
54029     title: "Login",
54030     modal: true,
54031     width:  350,
54032     //height: 230,
54033     height: 180,
54034     shadow: true,
54035     minWidth:200,
54036     minHeight:180,
54037     //proxyDrag: true,
54038     closable: false,
54039     draggable: false,
54040     collapsible: false,
54041     resizable: false,
54042     center: {  // needed??
54043         autoScroll:false,
54044         titlebar: false,
54045        // tabPosition: 'top',
54046         hideTabs: true,
54047         closeOnTab: true,
54048         alwaysShowTabs: false
54049     } ,
54050     listeners : {
54051         
54052         show  : function(dlg)
54053         {
54054             //console.log(this);
54055             this.form = this.layout.getRegion('center').activePanel.form;
54056             this.form.dialog = dlg;
54057             this.buttons[0].form = this.form;
54058             this.buttons[0].dialog = dlg;
54059             this.buttons[1].form = this.form;
54060             this.buttons[1].dialog = dlg;
54061            
54062            //this.resizeToLogo.defer(1000,this);
54063             // this is all related to resizing for logos..
54064             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
54065            //// if (!sz) {
54066              //   this.resizeToLogo.defer(1000,this);
54067              //   return;
54068            // }
54069             //var w = Ext.lib.Dom.getViewWidth() - 100;
54070             //var h = Ext.lib.Dom.getViewHeight() - 100;
54071             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
54072             //this.center();
54073             if (this.disabled) {
54074                 this.hide();
54075                 return;
54076             }
54077             
54078             if (this.user.id < 0) { // used for inital setup situations.
54079                 return;
54080             }
54081             
54082             if (this.intervalID) {
54083                 // remove the timer
54084                 window.clearInterval(this.intervalID);
54085                 this.intervalID = false;
54086             }
54087             
54088             
54089             if (Roo.get('loading')) {
54090                 Roo.get('loading').remove();
54091             }
54092             if (Roo.get('loading-mask')) {
54093                 Roo.get('loading-mask').hide();
54094             }
54095             
54096             //incomming._node = tnode;
54097             this.form.reset();
54098             //this.dialog.modal = !modal;
54099             //this.dialog.show();
54100             this.el.unmask(); 
54101             
54102             
54103             this.form.setValues({
54104                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
54105                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
54106             });
54107             
54108             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
54109             if (this.form.findField('username').getValue().length > 0 ){
54110                 this.form.findField('password').focus();
54111             } else {
54112                this.form.findField('username').focus();
54113             }
54114     
54115         }
54116     },
54117     items : [
54118          {
54119        
54120             xtype : 'ContentPanel',
54121             xns : Roo,
54122             region: 'center',
54123             fitToFrame : true,
54124             
54125             items : [
54126     
54127                 {
54128                
54129                     xtype : 'Form',
54130                     xns : Roo.form,
54131                     labelWidth: 100,
54132                     style : 'margin: 10px;',
54133                     
54134                     listeners : {
54135                         actionfailed : function(f, act) {
54136                             // form can return { errors: .... }
54137                                 
54138                             //act.result.errors // invalid form element list...
54139                             //act.result.errorMsg// invalid form element list...
54140                             
54141                             this.dialog.el.unmask();
54142                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
54143                                         "Login failed - communication error - try again.");
54144                                       
54145                         },
54146                         actioncomplete: function(re, act) {
54147                              
54148                             Roo.state.Manager.set(
54149                                 this.dialog.realm + '.username',  
54150                                     this.findField('username').getValue()
54151                             );
54152                             Roo.state.Manager.set(
54153                                 this.dialog.realm + '.lang',  
54154                                 this.findField('lang').getValue() 
54155                             );
54156                             
54157                             this.dialog.fillAuth(act.result.data);
54158                               
54159                             this.dialog.hide();
54160                             
54161                             if (Roo.get('loading-mask')) {
54162                                 Roo.get('loading-mask').show();
54163                             }
54164                             Roo.XComponent.build();
54165                             
54166                              
54167                             
54168                         }
54169                     },
54170                     items : [
54171                         {
54172                             xtype : 'TextField',
54173                             xns : Roo.form,
54174                             fieldLabel: "Email Address",
54175                             name: 'username',
54176                             width:200,
54177                             autoCreate : {tag: "input", type: "text", size: "20"}
54178                         },
54179                         {
54180                             xtype : 'TextField',
54181                             xns : Roo.form,
54182                             fieldLabel: "Password",
54183                             inputType: 'password',
54184                             name: 'password',
54185                             width:200,
54186                             autoCreate : {tag: "input", type: "text", size: "20"},
54187                             listeners : {
54188                                 specialkey : function(e,ev) {
54189                                     if (ev.keyCode == 13) {
54190                                         this.form.dialog.el.mask("Logging in");
54191                                         this.form.doAction('submit', {
54192                                             url: this.form.dialog.url,
54193                                             method: this.form.dialog.method
54194                                         });
54195                                     }
54196                                 }
54197                             }  
54198                         },
54199                         {
54200                             xtype : 'ComboBox',
54201                             xns : Roo.form,
54202                             fieldLabel: "Language",
54203                             name : 'langdisp',
54204                             store: {
54205                                 xtype : 'SimpleStore',
54206                                 fields: ['lang', 'ldisp'],
54207                                 data : [
54208                                     [ 'en', 'English' ],
54209                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
54210                                     [ 'zh_CN', '\u7C21\u4E2D' ]
54211                                 ]
54212                             },
54213                             
54214                             valueField : 'lang',
54215                             hiddenName:  'lang',
54216                             width: 200,
54217                             displayField:'ldisp',
54218                             typeAhead: false,
54219                             editable: false,
54220                             mode: 'local',
54221                             triggerAction: 'all',
54222                             emptyText:'Select a Language...',
54223                             selectOnFocus:true,
54224                             listeners : {
54225                                 select :  function(cb, rec, ix) {
54226                                     this.form.switchLang(rec.data.lang);
54227                                 }
54228                             }
54229                         
54230                         }
54231                     ]
54232                 }
54233                   
54234                 
54235             ]
54236         }
54237     ],
54238     buttons : [
54239         {
54240             xtype : 'Button',
54241             xns : 'Roo',
54242             text : "Forgot Password",
54243             listeners : {
54244                 click : function() {
54245                     //console.log(this);
54246                     var n = this.form.findField('username').getValue();
54247                     if (!n.length) {
54248                         Roo.MessageBox.alert("Error", "Fill in your email address");
54249                         return;
54250                     }
54251                     Roo.Ajax.request({
54252                         url: this.dialog.url,
54253                         params: {
54254                             passwordRequest: n
54255                         },
54256                         method: this.dialog.method,
54257                         success:  function(response, opts)  {  // check successfull...
54258                         
54259                             var res = this.dialog.processResponse(response);
54260                             if (!res.success) { // error!
54261                                Roo.MessageBox.alert("Error" ,
54262                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
54263                                return;
54264                             }
54265                             Roo.MessageBox.alert("Notice" ,
54266                                 "Please check you email for the Password Reset message");
54267                         },
54268                         failure : function() {
54269                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
54270                         }
54271                         
54272                     });
54273                 }
54274             }
54275         },
54276         {
54277             xtype : 'Button',
54278             xns : 'Roo',
54279             text : "Login",
54280             listeners : {
54281                 
54282                 click : function () {
54283                         
54284                     this.dialog.el.mask("Logging in");
54285                     this.form.doAction('submit', {
54286                             url: this.dialog.url,
54287                             method: this.dialog.method
54288                     });
54289                 }
54290             }
54291         }
54292     ]
54293   
54294   
54295 })
54296  
54297
54298
54299